Export BuildCodegenCLITask to its own task class

Summary:
This diff moves `BuildCodegenCLITask` from being inlined to have its own task definition
I've also cleaned up the task body and refactored a bit how we do the CLI invocation.

Changelog:
[Internal] [Changed] - Export `BuildCodegenCLITask` to its own task class

Reviewed By: ShikaSD

Differential Revision: D31016610

fbshipit-source-id: 8407fc2a62ea473daa775196925c1b9b6f4f2f4b
This commit is contained in:
Nicola Corti
2021-09-17 11:26:30 -07:00
committed by Facebook GitHub Bot
parent cc59a7cbde
commit 2a0d8cccea
6 changed files with 123 additions and 46 deletions
@@ -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<Exec> buildCodegenTask =
TaskProvider<BuildCodegenCLITask> 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
@@ -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<String>
@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"
}
}
@@ -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
}
}
}
@@ -13,3 +13,13 @@ internal fun windowsAwareCommandLine(vararg args: Any): List<Any> =
} else {
args.toList()
}
internal fun windowsAwareBashCommandLine(
vararg args: String,
bashWindowsHome: String? = null
): List<String> =
if (Os.isWindows()) {
listOf(bashWindowsHome ?: "bash", "-c") + args
} else {
args.toList()
}
@@ -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())
}
}
@@ -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"))
}
}