All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mill.javalib.palantirformat.PalantirFormatModule.scala Maven / Gradle / Ivy

The newest version!
package mill
package javalib.palantirformat

import mill.api.{Loose, PathRef}
import mill.define.{Discover, ExternalModule}
import mill.main.Tasks
import mill.scalalib.{CoursierModule, DepSyntax, JavaModule}
import mill.util.Jvm

trait PalantirFormatBaseModule extends CoursierModule {

  /**
   * Classpath for running Palantir Java Format.
   */
  def palantirformatClasspath: T[Loose.Agg[PathRef]] = Task {
    defaultResolver().classpath(
      Agg(ivy"com.palantir.javaformat:palantir-java-format:${palantirformatVersion()}")
    )
  }

  /**
   * JVM arguments for running Palantir Java Format. Defaults to values prescribed in
   * "[[https://github.com/palantir/palantir-java-format/issues/548 Broken on Java 16]]".
   */
  def palantirformatJvmArgs: T[Seq[String]] = Task {
    Seq(
      "--add-exports",
      "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
      "--add-exports",
      "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED",
      "--add-exports",
      "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED",
      "--add-exports",
      "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
      "--add-exports",
      "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"
    )
  }

  /**
   * Path to options file for Palantir Java Format CLI. Defaults to `millSourcePath` `/` `palantirformat.options`.
   */
  def palantirformatOptions: T[PathRef] = Task.Source(
    millSourcePath / "palantirformat.options"
  )

  /**
   * Palantir Java Format version. Defaults to `2.50.0`.
   */
  def palantirformatVersion: T[String] = Task {
    "2.50.0"
  }
}

/**
 * Formats Java source files using [[https://github.com/palantir/palantir-java-format Palantir Java Format]].
 */
trait PalantirFormatModule extends JavaModule with PalantirFormatBaseModule {

  /**
   * Formats Java source files.
   *
   * @param check if an exception should be raised when formatting errors are found
   *              - when set, files are not formatted
   * @param sources list of file or folder path(s) to be processed
   *                - path must be relative to [[millSourcePath]]
   *                - when empty, all [[sources]] are processed
   */
  def palantirformat(
      check: mainargs.Flag = mainargs.Flag(value = false),
      sources: mainargs.Leftover[String]
  ): Command[Unit] = Task.Command {

    val _sources =
      if (sources.value.isEmpty) this.sources()
      else sources.value.iterator.map(rel => PathRef(millSourcePath / os.RelPath(rel)))

    PalantirFormatModule.palantirAction(
      _sources,
      check.value,
      palantirformatOptions(),
      palantirformatClasspath(),
      palantirformatJvmArgs()
    )
  }
}
object PalantirFormatModule extends ExternalModule with PalantirFormatBaseModule with TaskModule {

  override def defaultCommandName(): String = "formatAll"

  /**
   * Formats Java source files.
   *
   * @param check if an exception should be raised when formatting errors are found
   *              - when set, files are not formatted
   * @param sources list of [[JavaModule]] sources to process
   */
  def formatAll(
      check: mainargs.Flag = mainargs.Flag(value = false),
      @mainargs.arg(positional = true) sources: Tasks[Seq[PathRef]] =
        Tasks.resolveMainDefault("__.sources")
  ): Command[Unit] = Task.Command {

    val _sources = Task.sequence(sources.value)().iterator.flatten

    palantirAction(
      _sources,
      check.value,
      palantirformatOptions(),
      palantirformatClasspath(),
      palantirformatJvmArgs()
    )
  }

  lazy val millDiscover: Discover = Discover[this.type]

  private[palantirformat] def palantirAction(
      sources: IterableOnce[PathRef],
      check: Boolean,
      options: PathRef,
      classPath: Loose.Agg[PathRef],
      jvmArgs: Seq[String]
  )(implicit ctx: api.Ctx): Unit = {

    if (check) {
      ctx.log.info("checking format in java sources ...")
    } else {
      ctx.log.info("formatting java sources ...")
    }

    palantirArgs(sources, check, options) match {
      case None =>

        ctx.log.debug("source folder not found, skipping")
      case Some(mainArgs) =>

        ctx.log.debug(s"running palantirformat with $mainArgs")

        val exitCode = Jvm.callProcess(
          mainClass = "com.palantir.javaformat.java.Main",
          classPath = classPath.map(_.path).toVector,
          jvmArgs = jvmArgs,
          mainArgs = mainArgs,
          cwd = ctx.dest,
          stdin = os.Inherit,
          stdout = os.Inherit,
          check = false
        ).exitCode

        if (check && exitCode != 0) {
          ctx.log.error(
            "palantirformat aborted due to format error(s) (or invalid plugin settings/palantirformat options)"
          )
          throw new RuntimeException(s"palantirformat exit($exitCode)")
        }
    }

  }

  private def palantirArgs(
      sources: IterableOnce[PathRef],
      check: Boolean,
      options: PathRef
  ): Option[Seq[String]] = {

    val sourceArgs = sources
      .iterator
      .map(_.path)
      .filter(os.exists(_))
      .flatMap(os.walk(_, includeTarget = true))
      .filter(os.isFile)
      .filter(_.ext == "java")
      .map(_.toString())
      .toSeq

    Option.when(sourceArgs.nonEmpty) {
      val args = Seq.newBuilder[String]

      // https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java#L199
      if (os.exists(options.path)) args += s"@${options.path}"

      // https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptions.java#L27
      if (check) {
        // do not overwrite files and exit(1) if formatting changes were detected
        args += "--dry-run" += "--set-exit-if-changed"
      } else {
        // format in place
        args += "--replace"
      }

      // https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java#L49
      args ++= sourceArgs

      args.result()
    }
  }

  /**
   * Path to options file for Palantir Java Format CLI at `Task.workspace` `/` `palantirformat.options`.
   */
  override def palantirformatOptions: T[PathRef] = Task.Source(
    Task.workspace / "palantirformat.options"
  )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy