Add Kotlin formatter

Content of README.google:

Name: Tool to reformat Kotlin source code to comply with Google Kotlin Style.
URL: https://github.com/facebook/ktfmt
Version: 0.54
Revision: NA
Date: January 15th 2025
License: Apache 2.0, see LICENSE

Downloaded files

  * ktfmt-0.54-jar-with-dependencies.jar
  * LICENSE

Change-Id: Idea26c37fa5a8fa41296b5736b379763780e8c2b
diff --git a/.gitignore b/.gitignore
index 8bd1412..57f3528 100644
--- a/.gitignore
+++ b/.gitignore
@@ -115,6 +115,8 @@
 third_party/gmscore/*
 third_party/google/google-java-format/1.24.0
 third_party/google/google-java-format/1.24.0.tar.gz
+third_party/google/google-kotlin-format/0.54
+third_party/google/google-kotlin-format/0.52.tar.gz
 third_party/google/yapf/20231013
 third_party/google/yapf/20231013.tar.gz
 third_party/google-java-format
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index 2c9dc74..b8d60f8 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -629,6 +629,10 @@
     "google-java-format-1.24",
     Paths.get("third_party", "google", "google-java-format", "1.24.0").toFile(),
     Paths.get("third_party", "google", "google-java-format", "1.24.0.tar.gz.sha1").toFile())
+  val googleKotlinFormat_0_54 = ThirdPartyDependency(
+    "google-kotlin-format-0.54",
+    Paths.get("third_party", "google", "google-kotlin-format", "0.54").toFile(),
+    Paths.get("third_party", "google", "google-kotlin-format", "0.54.tar.gz.sha1").toFile())
   val googleYapf_20231013 = ThirdPartyDependency(
     "google-yapf-20231013",
     Paths.get("third_party", "google", "yapf", "20231013").toFile(),
diff --git a/src/test/java/com/android/tools/r8/androidapi/GenerateAvailableApiExceptions.java b/src/test/java/com/android/tools/r8/androidapi/GenerateAvailableApiExceptions.java
index b6a1c72..fc1629b 100644
--- a/src/test/java/com/android/tools/r8/androidapi/GenerateAvailableApiExceptions.java
+++ b/src/test/java/com/android/tools/r8/androidapi/GenerateAvailableApiExceptions.java
@@ -84,7 +84,7 @@
     String rawOutput = builder.toString();
     Path tempFile = Files.createTempFile("output-", ".java");
     FileUtils.writeTextFile(tempFile, rawOutput);
-    return MethodGenerationBase.formatRawOutput(tempFile);
+    return MethodGenerationBase.javaFormatRawOutput(tempFile);
   }
 
   private static boolean isThrowable(
diff --git a/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java b/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
index 469b5cd..9c873c5 100644
--- a/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/androidapi/GenerateCovariantReturnTypeMethodsTest.java
@@ -144,7 +144,7 @@
             .toString();
     Path tempFile = Files.createTempFile("output-", ".java");
     Files.write(tempFile, javaSourceCode.getBytes(StandardCharsets.UTF_8));
-    return MethodGenerationBase.formatRawOutput(tempFile);
+    return MethodGenerationBase.javaFormatRawOutput(tempFile);
   }
 
   private static void registerCovariantMethod(
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
index 3b84f5e..8db8c25 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/CfClassGenerator.java
@@ -33,7 +33,7 @@
   public abstract Class<?> getImplementation();
 
   public String generateClass() throws IOException {
-    return formatRawOutput(generateRawOutput());
+    return javaFormatRawOutput(generateRawOutput());
   }
 
   private String generateRawOutput() throws IOException {
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
index 2925557..0bf9bd4 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/CodeGenerationBase.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.ToolHelper.ProcessResult;
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.utils.FileUtils;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
 import java.io.File;
@@ -19,22 +20,61 @@
 
 public abstract class CodeGenerationBase extends TestBase {
 
-  private static final Path GOOGLE_FORMAT_DIR =
+  private static final Path GOOGLE_KOTLIN_FORMAT_DIR =
+      Paths.get(ToolHelper.THIRD_PARTY_DIR, "google", "google-kotlin-format", "0.54");
+  private static final Path GOOGLE_KOTLIN_FORMAT_JAR =
+      GOOGLE_KOTLIN_FORMAT_DIR.resolve("ktfmt-0.54-jar-with-dependencies.jar");
+  private static final Path GOOGLE_JAVA_FORMAT_DIR =
       Paths.get(ToolHelper.THIRD_PARTY_DIR, "google", "google-java-format", "1.24.0");
-  private static final Path GOOGLE_FORMAT_JAR =
-      GOOGLE_FORMAT_DIR.resolve("google-java-format-1.24.0-all-deps.jar");
+  private static final Path GOOGLE_JAVA_FORMAT_JAR =
+      GOOGLE_JAVA_FORMAT_DIR.resolve("google-java-format-1.24.0-all-deps.jar");
 
   protected final DexItemFactory factory = new DexItemFactory();
 
-  public static String formatRawOutput(String rawOutput) throws IOException {
+  public static String kotlinFormatRawOutput(String rawOutput) throws IOException {
+    Path temporaryFile = File.createTempFile("output-", ".kt").toPath();
+    Files.write(temporaryFile, rawOutput.getBytes());
+    kotlinFormatRawOutput(temporaryFile);
+    String result = FileUtils.readTextFile(temporaryFile);
+    temporaryFile.toFile().deleteOnExit();
+    return result;
+  }
+
+  public static String javaFormatRawOutput(String rawOutput) throws IOException {
     File temporaryFile = File.createTempFile("output-", ".java");
     Files.write(temporaryFile.toPath(), rawOutput.getBytes());
-    String result = formatRawOutput(temporaryFile.toPath());
+    String result = javaFormatRawOutput(temporaryFile.toPath());
     temporaryFile.deleteOnExit();
     return result;
   }
 
-  public static String formatRawOutput(Path tempFile) throws IOException {
+  public static String kotlinFormatRawOutput(Path tempFile) throws IOException {
+    // Apply google format.
+    ProcessBuilder builder =
+        new ProcessBuilder(
+            ImmutableList.of(
+                getJavaExecutable(),
+                "-jar",
+                GOOGLE_KOTLIN_FORMAT_JAR.toString(),
+                "--google-style",
+                tempFile.toAbsolutePath().toString()));
+    String commandString = String.join(" ", builder.command());
+    System.out.println(commandString);
+    Process process = builder.start();
+    ProcessResult result = ToolHelper.drainProcessOutputStreams(process, commandString);
+    // Kotlin formatter will write "Done formatting..." to stderr.
+    if (result.exitCode != 0) {
+      throw new IllegalStateException(result.toString());
+    }
+    // Fix line separators.
+    String content = result.stdout;
+    if (!StringUtils.LINE_SEPARATOR.equals("\n")) {
+      return content.replace(StringUtils.LINE_SEPARATOR, "\n");
+    }
+    return content;
+  }
+
+  public static String javaFormatRawOutput(Path tempFile) throws IOException {
     // Apply google format.
     ProcessBuilder builder =
         new ProcessBuilder(
@@ -47,7 +87,7 @@
                 "--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
                 "--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
                 "-jar",
-                GOOGLE_FORMAT_JAR.toString(),
+                GOOGLE_JAVA_FORMAT_JAR.toString(),
                 tempFile.toAbsolutePath().toString()));
     String commandString = String.join(" ", builder.command());
     System.out.println(commandString);
diff --git a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
index e10ab64..29bdf68 100644
--- a/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
+++ b/src/test/java/com/android/tools/r8/cfmethodgeneration/MethodGenerationBase.java
@@ -85,7 +85,7 @@
     List<DexEncodedField> fields = new ArrayList<>();
     readMethodTemplatesInto(codePrinter, generatedMethods::put, fields::add);
     generateRawOutput(generatedMethods, fields, codePrinter, tempFile.toPath());
-    String result = formatRawOutput(tempFile.toPath());
+    String result = javaFormatRawOutput(tempFile.toPath());
 
     tempFile.deleteOnExit();
     return result;
diff --git a/third_party/google/google-kotlin-format/0.54.tar.gz.sha1 b/third_party/google/google-kotlin-format/0.54.tar.gz.sha1
new file mode 100644
index 0000000..db389a8
--- /dev/null
+++ b/third_party/google/google-kotlin-format/0.54.tar.gz.sha1
@@ -0,0 +1 @@
+3c6048ca6f062bc0160d499af65287273ac771d3
\ No newline at end of file