react.gradle 11.1 KB
Newer Older
silver47gin's avatar
silver47gin committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
import org.apache.tools.ant.taskdefs.condition.Os

def config = project.hasProperty("react") ? project.react : [:];

/**
 * Detects CLI location in a similar fashion to the React Native CLI
 */
def detectCliPath(config) {
  if (config.cliPath) {
    return config.cliPath
  }
  if (new File("${projectDir}/../../node_modules/@tarojs/cli/index.js").exists()) {
    return "${projectDir}/../../node_modules/@tarojs/cli/index.js"
  }
  throw new Exception("Couldn't determine CLI location. " +
    "Please set `project.ext.react.cliPath` to the path of the @tarojs/cli cli.js");
}

def composeSourceMapsPath = config.composeSourceMapsPath ?: "node_modules/react-native/scripts/compose-source-maps.js"
def bundleAssetName = config.bundleAssetName ?: "index.android.bundle"
def bundleCommand = config.bundleCommand ?: "build"
def reactRoot = file(config.root ?: "../../")
def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"]
def bundleConfig = config.bundleConfig ? "${reactRoot}/${config.bundleConfig}" : null ;
def enableVmCleanup = config.enableVmCleanup == null ? true : config.enableVmCleanup
def hermesCommand = config.hermesCommand ?: "../../node_modules/hermes-engine/%OS-BIN%/hermesc"

def reactNativeDevServerPort() {
  def value = project.getProperties().get("reactNativeDevServerPort")
  return value != null ? value : "8081"
}

def reactNativeInspectorProxyPort() {
  def value = project.getProperties().get("reactNativeInspectorProxyPort")
  return value != null ? value : reactNativeDevServerPort()
}

def getHermesOSBin() {
  if (Os.isFamily(Os.FAMILY_WINDOWS)) return "win64-bin";
  if (Os.isFamily(Os.FAMILY_MAC)) return "osx-bin";
  if (Os.isOs(null, "linux", "amd64", null)) return "linux64-bin";
  throw new Exception("OS not recognized. Please set project.ext.react.hermesCommand " + "to the path of a working Hermes compiler.");
}

def getHermesCommand = {
    // If the project specifies a Hermes command, don't second guess it.
    if (!hermesCommand.contains("%OS-BIN%")) {
        return hermesCommand
    }
    // Execution on Windows fails with / as separator
    return hermesCommand
            .replaceAll("%OS-BIN%", getHermesOSBin())
            .replace('/' as char, File.separatorChar);
}

// Set enableHermesForVariant to a function to configure per variant,
// or set `enableHermes` to True/False to set all of them
def enableHermesForVariant = config.enableHermesForVariant ?: {
    def variant -> config.enableHermes ?: false
}

android {
    buildTypes.all {
        resValue "integer", "react_native_dev_server_port", reactNativeDevServerPort()
        resValue "integer", "react_native_inspector_proxy_port", reactNativeInspectorProxyPort()
    }
}

afterEvaluate {
    def isAndroidLibrary = plugins.hasPlugin("com.android.library")
    def variants = isAndroidLibrary ? android.libraryVariants : android.applicationVariants
    variants.all { def variant ->

        def targetName = variant.name.capitalize()
        def targetPath = variant.dirName

        // React js bundle directories
        def jsBundleDir = file("$buildDir/generated/assets/react/release")
        def resourcesDir = file("$buildDir/generated/res/react/release")

        def jsBundleFile = file("$jsBundleDir/$bundleAssetName")
        def jsSourceMapsDir = file("$buildDir/generated/assets/react/release")
        def jsIntermediateSourceMapsDir = file("$buildDir/intermediates/sourcemaps/react/${targetPath}")
        def jsPackagerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.packager.map")
        def jsCompilerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.compiler.map")
        def jsOutputSourceMapFile = file("$jsSourceMapsDir/${bundleAssetName}.map")

        def nodeExecutableAndArgs = config.nodeExecutableAndArgs ?: ["node"]
        def cliPath = detectCliPath(config)

        def execCommand = []

        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
            execCommand.addAll(["cmd", "/c", *nodeExecutableAndArgs, cliPath])
        } else {
            execCommand.addAll([*nodeExecutableAndArgs, cliPath])
        }

        def enableHermes = enableHermesForVariant(variant)

        def currentBundleTask = tasks.create(
            name: "bundle${targetName}JsAndAssets",
            type: Exec) {
            group = "react"
            description = "bundle JS and assets for ${targetName}."

            doFirst {
                jsBundleDir.deleteDir()
                jsBundleDir.mkdirs()
                resourcesDir.deleteDir()
                resourcesDir.mkdirs()
                jsIntermediateSourceMapsDir.deleteDir()
                jsIntermediateSourceMapsDir.mkdirs()
                jsSourceMapsDir.deleteDir()
                jsSourceMapsDir.mkdirs()
            }

            // Set up inputs and outputs so gradle can cache the result
            inputs.files fileTree(dir: reactRoot, excludes: inputExcludes)
            outputs.dir(jsBundleDir)
            outputs.dir(resourcesDir)

            workingDir(reactRoot)

            def devEnabled = !(config."devDisabledIn${targetName}"
                || targetName.toLowerCase().contains("release"))

            commandLine("npx", "taro", "build", "--type", "rn", "--platform", "android", "--reset-cache")

            enabled config."bundleIn${targetName}" != null
                ? config."bundleIn${targetName}"
                : config."bundleIn${variant.buildType.name.capitalize()}" != null
                    ? config."bundleIn${variant.buildType.name.capitalize()}"
                    : targetName.toLowerCase().contains("release")

        }

        variant.ext.bundleJsAndAssets = currentBundleTask
        currentBundleTask.ext.generatedResFolders = files(resourcesDir).builtBy(currentBundleTask)
        currentBundleTask.ext.generatedAssetsFolders = files(jsBundleDir).builtBy(currentBundleTask)

        // registerGeneratedResFolders for Android plugin 3.x
        if (variant.respondsTo("registerGeneratedResFolders")) {
            variant.registerGeneratedResFolders(currentBundleTask.generatedResFolders)
        } else {
            variant.registerResGeneratingTask(currentBundleTask)
        }
        variant.mergeResourcesProvider.get().dependsOn(currentBundleTask)

        // packageApplication for Android plugin 3.x
        def packageTask = variant.hasProperty("packageApplication")
            ? variant.packageApplicationProvider.get()
            : tasks.findByName("package${targetName}")
        if (variant.hasProperty("packageLibrary")) {
            packageTask = variant.packageLibrary
        }

        // pre bundle build task for Android plugin 3.2+
        def buildPreBundleTask = tasks.findByName("build${targetName}PreBundle")

        def resourcesDirConfigValue = config."resourcesDir${targetName}"
        if (resourcesDirConfigValue) {
            def currentCopyResTask = tasks.create(
                name: "copy${targetName}BundledResources",
                type: Copy) {
                group = "react"
                description = "copy bundled resources into custom location for ${targetName}."

                from(resourcesDir)
                into(file(resourcesDirConfigValue))

                dependsOn(currentBundleTask)

                enabled(currentBundleTask.enabled)
            }

            packageTask.dependsOn(currentCopyResTask)
            if (buildPreBundleTask != null) {
                buildPreBundleTask.dependsOn(currentCopyResTask)
            }
        }

        def currentAssetsCopyTask = tasks.create(
            name: "copy${targetName}BundledJs",
            type: Copy) {
            group = "react"
            description = "copy bundled JS into ${targetName}."

            if (config."jsBundleDir${targetName}") {
                from(jsBundleDir)
                into(file(config."jsBundleDir${targetName}"))
            } else {
                into ("$buildDir/intermediates")
                if (isAndroidLibrary) {
                    into ("library_assets/${variant.name}/out") {
                        from(jsBundleDir)
                    }
                } else {
                    into ("assets/${targetPath}") {
                        from(jsBundleDir)
                    }

                    // Workaround for Android Gradle Plugin 3.2+ new asset directory
                    into ("merged_assets/${variant.name}/merge${targetName}Assets/out") {
                        from(jsBundleDir)
                    }

                    // Workaround for Android Gradle Plugin 3.4+ new asset directory
                    into ("merged_assets/${variant.name}/out") {
                        from(jsBundleDir)
                    }
                }
            }

            // mergeAssets must run first, as it clears the intermediates directory
            dependsOn(variant.mergeAssetsProvider.get())

            enabled(currentBundleTask.enabled)
            dependsOn(currentBundleTask)
        }


        // mergeResources task runs before the bundle file is copied to the intermediate asset directory from Android plugin 4.1+.
        // This ensures to copy the bundle file before mergeResources task starts
        def mergeResourcesTask = tasks.findByName("merge${targetName}Resources")
        mergeResourcesTask.dependsOn(currentAssetsCopyTask)

        packageTask.dependsOn(currentAssetsCopyTask)
        if (buildPreBundleTask != null) {
            buildPreBundleTask.dependsOn(currentAssetsCopyTask)
        }

        // Delete the VM related libraries that this build doesn't need.
        // The application can manage this manually by setting 'enableVmCleanup: false'
        //
        // This should really be done by packaging all Hermes related libs into
        // two separate HermesDebug and HermesRelease AARs, but until then we'll
        // kludge it by deleting the .so files out of the /transforms/ directory.
        def isRelease = targetName.toLowerCase().contains("release")
        def libDir = "$buildDir/intermediates/transforms/"
        def vmSelectionAction = {
            fileTree(libDir).matching {
                if (enableHermes) {
                    // For Hermes, delete all the libjsc* files
                    include "**/libjsc*.so"

                    if (isRelease) {
                        // Reduce size by deleting the debugger/inspector
                        include '**/libhermes-inspector.so'
                        include '**/libhermes-executor-debug.so'
                    } else {
                        // Release libs take precedence and must be removed
                        // to allow debugging
                        include '**/libhermes-executor-release.so'
                    }
                } else {
                    // For JSC, delete all the libhermes* files
                    include "**/libhermes*.so"
                }
            }.visit { details ->
                def targetVariant = ".*/transforms/[^/]*/${targetPath}/.*"
                def path = details.file.getAbsolutePath().replace(File.separatorChar, '/' as char)
                if (path.matches(targetVariant) && details.file.isFile()) {
                    details.file.delete()
                }
            }
        }

        if (enableVmCleanup) {
            packageTask.doFirst(vmSelectionAction)
        }
    }
}