Skip to content

Instantly share code, notes, and snippets.

@zenz
Last active November 23, 2019 01:34
Show Gist options
  • Select an option

  • Save zenz/efd1f68ee4099abf77550ac7984df2fd to your computer and use it in GitHub Desktop.

Select an option

Save zenz/efd1f68ee4099abf77550ac7984df2fd to your computer and use it in GitHub Desktop.

Revisions

  1. zenz revised this gist Jul 30, 2019. 1 changed file with 14 additions and 24 deletions.
    38 changes: 14 additions & 24 deletions react.gradle
    Original file line number Diff line number Diff line change
    @@ -75,11 +75,7 @@ afterEvaluate {
    def jsBundleDir = file("$buildDir/generated/assets/react/${targetPath}")
    def resourcesDir = file("$buildDir/generated/res/react/${targetPath}")

    // def jsBundleFile = file("$jsBundleDir/$bundleAssetName")
    def jsBundleFile = file("$jsBundleDir/$bundleAssetName")
    def jsBundleFileDummy = file("$jsBundleDir/$bundleAssetName-dummy")
    def jsBundleFileMap = file("${jsBundleFile}.map")
    def jsBundleFileDummyMap = file("${jsBundleFileDummy}.map")
    def jsSourceMapsDir = file("$buildDir/generated/sourcemaps/react/${targetPath}")
    def jsIntermediateSourceMapsDir = file("$buildDir/intermediates/sourcemaps/react/${targetPath}")
    def jsPackagerSourceMapFile = file("$jsIntermediateSourceMapsDir/${bundleAssetName}.packager.map")
    @@ -143,42 +139,36 @@ afterEvaluate {
    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"]
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", jsBundleFileDummy, jsBundleFile, *hermesFlags)
    } else {
    commandLine(getHermesCommand(), "-emit-binary", "-out", jsBundleFileDummy, jsBundleFile, *hermesFlags)
    }
    } else {
    hermesFlags = config.hermesFlagsDebug
    if (hermesFlags == null) hermesFlags = []
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", jsBundleFile, jsBundleFile, *hermesFlags)
    } else {
    commandLine(getHermesCommand(), "-emit-binary", "-out", jsBundleFile, jsBundleFile, *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
    // NOTE: name coincides with jsOutputSourceMapFile
    file: "$jsBundleFileDummyMap",
    file: "${jsBundleFile}.hbc.map",
    tofile: jsCompilerSourceMapFile
    );
    ant.move(
    file: "$jsBundleFileDummy",
    tofile: "$jsBundleFile"
    );
    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)
    @@ -321,4 +311,4 @@ afterEvaluate {
    task.doFirst(vmSelectionAction)
    }
    }
    }
    }
  2. zenz created this gist Jul 26, 2019.
    324 changes: 324 additions & 0 deletions react.gradle
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,324 @@
    // 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 cliPath = config.cliPath ?: "node_modules/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 = config.entryFile ?: "index.android.js"
    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%/hermes"

    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 jsBundleFile = file("$jsBundleDir/$bundleAssetName")
    def jsBundleFileDummy = file("$jsBundleDir/$bundleAssetName-dummy")
    def jsBundleFileMap = file("${jsBundleFile}.map")
    def jsBundleFileDummyMap = file("${jsBundleFileDummy}.map")
    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 extraPackagerArgs = config.extraPackagerArgs ?: []

    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 = extraPackagerArgs;

    if (bundleConfig) {
    extraArgs = extraArgs.clone()
    extraArgs.add("--config");
    extraArgs.add(bundleConfig);
    }

    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    commandLine("cmd", "/c", *nodeExecutableAndArgs, cliPath, bundleCommand, "--platform", "android", "--dev", "${devEnabled}",
    "--reset-cache", "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir,
    "--sourcemap-output", enableHermes ? jsPackagerSourceMapFile : jsOutputSourceMapFile, *extraArgs)
    } else {
    commandLine(*nodeExecutableAndArgs, cliPath, 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;
    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"]
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", jsBundleFileDummy, jsBundleFile, *hermesFlags)
    } else {
    commandLine(getHermesCommand(), "-emit-binary", "-out", jsBundleFileDummy, jsBundleFile, *hermesFlags)
    }
    } else {
    hermesFlags = config.hermesFlagsDebug
    if (hermesFlags == null) hermesFlags = []
    if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    commandLine("cmd", "/c", getHermesCommand(), "-emit-binary", "-out", jsBundleFile, jsBundleFile, *hermesFlags)
    } else {
    commandLine(getHermesCommand(), "-emit-binary", "-out", jsBundleFile, jsBundleFile, *hermesFlags)
    }
    }
    //


    }
    if (hermesFlags.contains("-output-source-map")) {
    ant.move(
    // Hermes will generate a source map with this exact name
    // NOTE: name coincides with jsOutputSourceMapFile
    file: "$jsBundleFileDummyMap",
    tofile: jsCompilerSourceMapFile
    );
    ant.move(
    file: "$jsBundleFileDummy",
    tofile: "$jsBundleFile"
    );
    exec {
    // TODO: set task dependencies for caching
    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)
    }

    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)
    }
    }
    }