Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ScalaModule.scalacHelp command #2921

Merged
merged 4 commits into from
Dec 14, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 64 additions & 1 deletion scalalib/src/mill/scalalib/ScalaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import mill.util.{Jvm, Util}
import mill.util.Jvm.createJar
import mill.api.Loose.Agg
import mill.scalalib.api.{CompilationResult, Versions, ZincWorkerUtil}

import mainargs.Flag
import mill.scalalib.bsp.{BspBuildTarget, BspModule, ScalaBuildTarget, ScalaPlatform}
import mill.scalalib.dependency.versions.{ValidVersion, Version}

import scala.reflect.internal.util.ScalaClassLoader
import scala.util.Using

/**
* Core configuration required to compile a single Scala compilation target
*/
Expand Down Expand Up @@ -87,6 +89,67 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer =>
)
}

/**
* Print the scala compile built-in help output.
* This is equivalent to running `scalac -help`
*
* @param args The option to pass to the scala compiler, e.g. "-Xlint:help". Default: "-help"
*/
def scalacHelp(
@mainargs.arg(doc =
"""The option to pass to the scala compiler, e.g. "-Xlint:help". Default: "-help""""
)
args: String*
): Command[Unit] = T.command {
val sv = scalaVersion()

// TODO: do we need to handle compiler plugins?
val options: Seq[String] = if (args.isEmpty) Seq("-help") else args
T.log.info(
s"""Output of scalac version: ${sv}
| with options: ${options.mkString(" ")}
|""".stripMargin
)

// Zinc isn't outputting any help with `-help` options, so we ask the compiler directly
val cp = scalaCompilerClasspath()
Using.resource(ScalaClassLoader.fromURLs(cp.toSeq.map(_.path.toNIO.toUri().toURL()))) { cl =>
def handleResult(trueIsSuccess: Boolean): PartialFunction[Any, Result[Unit]] = {
val ok = Result.Success(())
val fail = Result.Failure("The compiler exited with errors (exit code 1)")

{
case true | java.lang.Boolean.TRUE => if (trueIsSuccess) ok else fail
case false | java.lang.Boolean.FALSE => if (trueIsSuccess) fail else ok
case null if sv.startsWith("2.") =>
// Scala 2.11 and earlier return `Unit` and require use to use the result value,
// which we don't want to implement for just a simple help output of an very old compiler
Result.Success(())
case x => Result.Failure(s"Got unexpected return type from the scala compiler: ${x}")
}
}

if (sv.startsWith("2.")) {
// Scala 2.x
val mainClass = cl.loadClass("scala.tools.nsc.Main")
val mainMethod = mainClass.getMethod("process", Seq(classOf[Array[String]]): _*)
val exitVal = mainMethod.invoke(null, options.toArray)
handleResult(true)(exitVal)
} else {
// Scala 3.x
val mainClass = cl.loadClass("dotty.tools.dotc.Main")
val mainMethod = mainClass.getMethod("process", Seq(classOf[Array[String]]): _*)
val resultClass = cl.loadClass("dotty.tools.dotc.reporting.Reporter")
val hasErrorsMethod = resultClass.getMethod("hasErrors")
val exitVal = mainMethod.invoke(null, options.toArray)
exitVal match {
case r if resultClass.isInstance(r) => handleResult(false)(hasErrorsMethod.invoke(r))
case x => Result.Failure(s"Got unexpected return type from the scala compiler: ${x}")
}
}
}
}

/**
* Allows you to make use of Scala compiler plugins.
*/
Expand Down