diff --git a/packages/react-native-gradle-plugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java b/packages/react-native-gradle-plugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java index d3b337c591f..468098fa256 100644 --- a/packages/react-native-gradle-plugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java +++ b/packages/react-native-gradle-plugin/src/main/java/com/facebook/react/codegen/plugin/CodegenPlugin.java @@ -10,13 +10,12 @@ package com.facebook.react.codegen.plugin; import com.android.build.gradle.BaseExtension; import com.facebook.react.ReactExtension; import com.facebook.react.codegen.generator.JavaGenerator; +import com.facebook.react.tasks.BuildCodegenCLITask; import com.facebook.react.utils.GradleUtils; import com.facebook.react.utils.PathUtils; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import java.io.File; -import java.io.IOException; -import org.apache.tools.ant.taskdefs.condition.Os; import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.Task; @@ -40,55 +39,16 @@ public class CodegenPlugin { // 2. Task: produce schema from JS files. String os = System.getProperty("os.name").toLowerCase(); - TaskProvider buildCodegenTask = + TaskProvider buildCodegenTask = project .getTasks() .register( "buildCodegenCLI", - Exec.class, + BuildCodegenCLITask.class, task -> { - // This task is required when using react-native-codegen from source, instead of - // npm. - File codegenRoot = extension.getCodegenDir().getAsFile().get(); - - task.getInputs() - .files( - project.file(new File(codegenRoot, "scripts")), - project.file(new File(codegenRoot, "src")), - project.file(new File(codegenRoot, "package.json")), - project.file(new File(codegenRoot, ".babelrc")), - project.file(new File(codegenRoot, ".prettierrc"))); - - File libDir = project.file(new File(codegenRoot, "lib")); - File nodeModulesDir = project.file(new File(codegenRoot, "node_modules")); - task.getOutputs().dirs(libDir, nodeModulesDir); - - task.onlyIf( - spec -> { - File cliDir = new File(codegenRoot, "lib/cli/"); - return !cliDir.exists() || cliDir.listFiles().length == 0; - }); - - if (Os.isFamily(Os.FAMILY_WINDOWS)) { - // Convert path to Linux format: use canonical path to strip it off relative - // elements in the middle of the string. - // Then replace baskslashes with slashes, remove leading colon, add leading - // slash. - // Eg. D:\path1\sub2/.. -> /D/path1/path2 - try { - String canonicalPath = codegenRoot.getCanonicalPath(); - String linuxPath = "/" + canonicalPath.replace('\\', '/').replace(":", ""); - - // Get the location of bash in the system; assume environment variable created - // to store it. - String bashHome = System.getenv("REACT_WINDOWS_BASH"); - task.commandLine(bashHome, "-c", linuxPath + "/scripts/oss/build.sh"); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - task.commandLine(codegenRoot.getAbsolutePath() + "/scripts/oss/build.sh"); - } + task.getCodegenDir().set(extension.getCodegenDir()); + String bashWindowsHome = (String) project.findProperty("REACT_WINDOWS_BASH"); + task.getBashWindowsHome().set(bashWindowsHome); }); project diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/BuildCodegenCLITask.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/BuildCodegenCLITask.kt new file mode 100644 index 00000000000..cafdad393d7 --- /dev/null +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/BuildCodegenCLITask.kt @@ -0,0 +1,56 @@ +/* + * 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. + */ + +package com.facebook.react.tasks + +import com.facebook.react.utils.Os.unixifyPath +import com.facebook.react.utils.windowsAwareBashCommandLine +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* + +/** + * A Task that will call the `scripts/oss/build.sh` script to trigger the creation of the codegen + * lib artifacts. + * + * NOTE: This task is required when using react-native-codegen from source, instead of npm. + */ +abstract class BuildCodegenCLITask : Exec() { + + @get:Internal abstract val codegenDir: DirectoryProperty + + @get:Internal abstract val bashWindowsHome: Property + + @get:InputFiles + val input: FileCollection = + codegenDir.files("scripts", "src", "package.json", ".babelrc", ".prettierrc") + + @get:OutputFiles val output: FileCollection = codegenDir.files("lib", "node_modules") + + init { + // We need this condition as we want a single instance of BuildCodegenCLITask to execute + // per project. Therefore we can safely skip the task if the lib/cli/ folder is available. + onlyIf { + val cliDir = codegenDir.file("lib/cli/").get().asFile + !cliDir.exists() || cliDir.listFiles()?.size == 0 + } + } + + override fun exec() { + commandLine( + windowsAwareBashCommandLine( + codegenDir.asFile.get().canonicalPath.unixifyPath().plus(BUILD_SCRIPT_PATH), + bashWindowsHome = bashWindowsHome.orNull, + )) + super.exec() + } + + companion object { + private const val BUILD_SCRIPT_PATH = "/scripts/oss/build.sh" + } +} diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/Os.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/Os.kt index 15ca729f27e..d677d2d9591 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/Os.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/Os.kt @@ -13,4 +13,13 @@ object Os { fun isWindows(): Boolean = System.getProperty("os.name")?.toLowerCase(Locale.ROOT)?.contains("windows") ?: false + + fun String.unixifyPath() = + this.replace('\\', '/').replace(":", "").let { + if (!it.startsWith("/")) { + "/$it" + } else { + it + } + } } diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/TaskUtils.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/TaskUtils.kt index d64cbf79c4e..514d3aac733 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/TaskUtils.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/TaskUtils.kt @@ -13,3 +13,13 @@ internal fun windowsAwareCommandLine(vararg args: Any): List = } else { args.toList() } + +internal fun windowsAwareBashCommandLine( + vararg args: String, + bashWindowsHome: String? = null +): List = + if (Os.isWindows()) { + listOf(bashWindowsHome ?: "bash", "-c") + args + } else { + args.toList() + } diff --git a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/OsTest.kt b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/OsTest.kt index 2311d356a25..987a6d84163 100644 --- a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/OsTest.kt +++ b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/OsTest.kt @@ -10,6 +10,7 @@ package com.facebook.react.utils import com.facebook.react.tests.OS import com.facebook.react.tests.OsRule import com.facebook.react.tests.WithOs +import com.facebook.react.utils.Os.unixifyPath import org.junit.Assert.* import org.junit.Rule import org.junit.Test @@ -35,4 +36,18 @@ class OsTest { fun isWindows_onWindows_returnsTrue() { assertTrue(Os.isWindows()) } + + @Test + fun unixifyPath_withAUnixPath_doesNothing() { + val aUnixPath = "/just/a/unix/path.sh" + + assertEquals(aUnixPath, aUnixPath.unixifyPath()) + } + + @Test + fun unixifyPath_withAWindowsPath_convertsItCorrectly() { + val aWindowsPath = "D:\\just\\a\\windows\\path\\" + + assertEquals("/D/just/a/windows/path/", aWindowsPath.unixifyPath()) + } } diff --git a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/TaskUtilsTest.kt b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/TaskUtilsTest.kt index 7ca730cba11..81221c7a062 100644 --- a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/TaskUtilsTest.kt +++ b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/TaskUtilsTest.kt @@ -41,4 +41,31 @@ class TaskUtilsTest { fun windowsAwareCommandLine_onWindows_prependsCmd() { assertEquals(listOf("cmd", "/c", "a", "b", "c"), windowsAwareCommandLine("a", "b", "c")) } + + @Test + @WithOs(OS.MAC) + fun windowsAwareBashCommandLine_onMac_returnsTheList() { + assertEquals( + listOf("a", "b", "c"), windowsAwareBashCommandLine("a", "b", "c", bashWindowsHome = "abc")) + } + + @Test + @WithOs(OS.UNIX) + fun windowsAwareBashCommandLine_onLinux_returnsTheList() { + assertEquals(listOf("a", "b", "c"), windowsAwareBashCommandLine("a", "b", "c")) + } + + @Test + @WithOs(OS.WIN) + fun windowsAwareBashCommandLine_onWindows_prependsBash() { + assertEquals(listOf("bash", "-c", "a", "b", "c"), windowsAwareBashCommandLine("a", "b", "c")) + } + + @Test + @WithOs(OS.WIN) + fun windowsAwareBashCommandLine_onWindows_prependsCustomBashPath() { + assertEquals( + listOf("/custom/bash", "-c", "a", "b", "c"), + windowsAwareBashCommandLine("a", "b", "c", bashWindowsHome = "/custom/bash")) + } }