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

mill.contrib.checkstyle.CheckstyleModule.scala Maven / Gradle / Ivy

The newest version!
package mill
package contrib.checkstyle

import mill.api.{Loose, PathRef}
import mill.scalalib.{DepSyntax, JavaModule}
import mill.util.Jvm

/**
 * Performs quality checks on Java source files using [[https://checkstyle.org/ Checkstyle]].
 */
trait CheckstyleModule extends JavaModule {

  /**
   * Runs [[https://checkstyle.org/cmdline.html#Command_line_usage Checkstyle]] and returns one of
   *  - number of violations found
   *  - program exit code
   *
   * @note [[sources]] are processed when no [[CheckstyleArgs.sources]] are specified.
   */
  def checkstyle(@mainargs.arg checkstyleArgs: CheckstyleArgs): Command[Int] = Task.Command {
    val (output, exitCode) = checkstyle0(checkstyleArgs.stdout, checkstyleArgs.sources)()

    checkstyleHandleErrors(checkstyleArgs.stdout, checkstyleArgs.check, exitCode, output)
  }

  protected def checkstyle0(stdout: Boolean, leftover: mainargs.Leftover[String]) = Task.Anon {

    val output = checkstyleOutput().path
    val args = checkstyleOptions() ++
      Seq("-c", checkstyleConfig().path.toString()) ++
      Seq("-f", checkstyleFormat()) ++
      (if (stdout) Seq.empty else Seq("-o", output.toString())) ++
      (if (leftover.value.nonEmpty) leftover.value else sources().map(_.path.toString()))

    T.log.info("running checkstyle ...")
    T.log.debug(s"with $args")

    val exitCode = Jvm.callSubprocess(
      mainClass = "com.puppycrawl.tools.checkstyle.Main",
      classPath = checkstyleClasspath().map(_.path),
      mainArgs = args,
      workingDir = millSourcePath, // allow passing relative paths for sources like src/a/b
      streamOut = true,
      check = false
    ).exitCode

    (output, exitCode)
  }

  protected def checkstyleHandleErrors(
      stdout: Boolean,
      check: Boolean,
      exitCode: Int,
      output: os.Path
  )(implicit ctx: mill.api.Ctx): Int = {

    val reported = os.exists(output)
    if (reported) {
      T.log.info(s"checkstyle output report at $output")
    }

    if (exitCode == 0) {} // do nothing
    else if (exitCode < 0 || !(reported || stdout)) {
      T.log.error(
        s"checkstyle exit($exitCode); please check command arguments, plugin settings or try with another version"
      )
      throw new UnsupportedOperationException(s"checkstyle exit($exitCode)")
    } else if (check) {
      throw new RuntimeException(s"checkstyle found $exitCode violation(s)")
    } else {
      T.log.error(s"checkstyle found $exitCode violation(s)")
    }

    exitCode
  }

  /**
   * Classpath for running Checkstyle.
   */
  def checkstyleClasspath: T[Loose.Agg[PathRef]] = Task {
    defaultResolver().resolveDeps(
      Agg(ivy"com.puppycrawl.tools:checkstyle:${checkstyleVersion()}")
    )
  }

  /**
   * Checkstyle configuration file. Defaults to `checkstyle-config.xml`.
   */
  def checkstyleConfig: T[PathRef] = Task {
    PathRef(T.workspace / "checkstyle-config.xml")
  }

  /**
   * Checkstyle output format (` plain | sarif | xml `). Defaults to `plain`.
   */
  def checkstyleFormat: T[String] = Task {
    "plain"
  }

  /**
   * Additional arguments for Checkstyle.
   */
  def checkstyleOptions: T[Seq[String]] = Task {
    Seq.empty[String]
  }

  /**
   * Checkstyle output report.
   */
  def checkstyleOutput: T[PathRef] = Task {
    PathRef(T.dest / s"checkstyle-output.${checkstyleFormat()}")
  }

  /**
   * Checkstyle version.
   */
  def checkstyleVersion: T[String] = Task {
    "10.18.1"
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy