123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- /*
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- import org.apache.tools.ant.taskdefs.condition.Os
- def config = project.hasProperty("react") ? project.react : [:];
- def detectEntryFile(config) {
- if (System.getenv('ENTRY_FILE')) {
- return System.getenv('ENTRY_FILE')
- } else if (config.entryFile) {
- return config.entryFile
- } else if ((new File("${projectDir}/../../index.android.js")).exists()) {
- return "index.android.js"
- }
- return "index.js";
- }
- /**
- * Detects CLI location in a similar fashion to the React Native CLI
- */
- def detectCliPath(config) {
- if (config.cliPath) {
- return config.cliPath
- }
- def cliPath = ["node", "-e", "console.log(require('react-native/cli').bin);"].execute([], projectDir).text.trim()
-
- if (cliPath) {
- return cliPath
- } else if (new File("${projectDir}/../../node_modules/react-native/cli.js").exists()) {
- return "${projectDir}/../../node_modules/react-native/cli.js"
- } else {
- throw new Exception("Couldn't determine CLI location. " +
- "Please set `project.ext.react.cliPath` to the path of the react-native cli.js");
- }
- }
- def composeSourceMapsPath = config.composeSourceMapsPath ?: "node_modules/react-native/scripts/compose-source-maps.js"
- def bundleAssetName = config.bundleAssetName ?: "index.android.bundle"
- def entryFile = detectEntryFile(config)
- def bundleCommand = config.bundleCommand ?: "bundle"
- 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.");
- }
- // Make sure not to inspect the Hermes config unless we need it,
- // to avoid breaking any JSC-only setups.
- 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 ->
- // Create variant and target names
- def targetName = variant.name.capitalize()
- def targetPath = variant.dirName
- // React js bundle directories
- def jsBundleDir = file("$buildDir/generated/assets/react/${targetPath}")
- def resourcesDir = file("$buildDir/generated/res/react/${targetPath}")
- def jsBundleFile = file("$jsBundleDir/$bundleAssetName")
- def jsSourceMapsDir = file("$buildDir/generated/sourcemaps/react/${targetPath}")
- 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")
- // Additional node and packager commandline arguments
- 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}."
- // Create dirs if they are not there (e.g. the "clean" task just ran)
- 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)
- // Set up the call to the react-native cli
- workingDir(reactRoot)
- // Set up dev mode
- def devEnabled = !(config."devDisabledIn${targetName}"
- || targetName.toLowerCase().contains("release"))
- def extraArgs = config.extraPackagerArgs ?: [];
- if (bundleConfig) {
- extraArgs = extraArgs.clone()
- extraArgs.add("--config");
- extraArgs.add(bundleConfig);
- }
- commandLine(*execCommand, bundleCommand, "--platform", "android", "--dev", "${devEnabled}",
- "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir,
- "--sourcemap-output", enableHermes ? jsPackagerSourceMapFile : jsOutputSourceMapFile, *extraArgs)
- if (enableHermes) {
- doLast {
- def hermesFlags;
- def hbcTempFile = file("${jsBundleFile}.hbc")
- exec {
- if (targetName.toLowerCase().contains("release")) {
- // Can't use ?: since that will also substitute valid empty lists
- hermesFlags = config.hermesFlagsRelease
- if (hermesFlags == null) hermesFlags = ["-O", "-output-source-map"]
- } else {
- hermesFlags = config.hermesFlagsDebug
- if (hermesFlags == null) hermesFlags = []
- }
- if (Os.isFamily(Os.FAMILY_WINDOWS)) {
- commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
- } else {
- commandLine(getHermesCommand(), "-emit-binary", "-out", hbcTempFile, jsBundleFile, *hermesFlags)
- }
- }
- ant.move(
- file: hbcTempFile,
- toFile: jsBundleFile
- );
- if (hermesFlags.contains("-output-source-map")) {
- ant.move(
- // Hermes will generate a source map with this exact name
- file: "${jsBundleFile}.hbc.map",
- tofile: jsCompilerSourceMapFile
- );
- exec {
- // TODO: set task dependencies for caching
- // Set up the call to the compose-source-maps script
- workingDir(reactRoot)
- if (Os.isFamily(Os.FAMILY_WINDOWS)) {
- commandLine("cmd", "/c", *nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile)
- } else {
- commandLine(*nodeExecutableAndArgs, composeSourceMapsPath, jsPackagerSourceMapFile, jsCompilerSourceMapFile, "-o", jsOutputSourceMapFile)
- }
- }
- }
- }
- }
- 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")
- }
- // Expose a minimal interface on the application variant and the task itself:
- 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")
- 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)
- }
- // 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 releated 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) {
- def task = tasks.findByName("package${targetName}")
- task.doFirst(vmSelectionAction)
- }
- }
- }
|