
scala.build.compiler.SimpleScalaCompiler.scala Maven / Gradle / Ivy
package scala.build.compiler
import java.io.File
import scala.build.internal.{Constants, Runner}
import scala.build.{Logger, Positioned, Project}
/** A simple Scala compiler designed to handle scaladocs, Java projects & get `scalac` outputs.
*
* @param defaultJavaCommand
* the default `java` command to be used
* @param defaultJavaOptions
* the default jvm options to be used with the `java` command
* @param scaladoc
* a flag for setting whether this compiler will handle scaladocs
*/
final case class SimpleScalaCompiler(
defaultJavaCommand: String,
defaultJavaOptions: Seq[String],
scaladoc: Boolean
) extends ScalaCompiler {
def jvmVersion: Option[Positioned[Int]] =
None // ??? TODO
def prepareProject(
project: Project,
logger: Logger
): Boolean =
// no incremental compilation, always compiling everything every time
true
override def usesClassDir: Boolean =
!scaladoc
/** Run a synthetic (created in runtime) `scalac` as a JVM process with the specified parameters
*
* @param mainClass
* the main class of the synthetic Scala compiler
* @param javaHomeOpt
* Java home path (optional)
* @param javacOptions
* options to be passed for the Java compiler
* @param scalacOptions
* options to be passed for the Scala compiler
* @param classPath
* class path to be passed to `scalac`
* @param compilerClassPath
* class path for the Scala compiler itself
* @param sources
* sources to be passed when running `scalac` (optional)
* @param outputDir
* output directory for the compiler (optional)
* @param argsFileDir
* output directory for the args file (optional)
* @param cwd
* working directory for running the compiler
* @param logger
* logger
* @return
* compiler process exit code
*/
private def runScalacLike(
mainClass: String,
javaHomeOpt: Option[os.Path],
javacOptions: Seq[String],
scalacOptions: Seq[String],
classPath: Seq[os.Path],
compilerClassPath: Seq[os.Path],
sources: Seq[String],
outputDir: Option[os.Path],
argsFilePath: Option[os.Path],
cwd: os.Path,
logger: Logger
): Int = {
outputDir.foreach(os.makeDir.all(_))
// initially adapted from https://github.com/VirtusLab/scala-cli/pull/103/files#diff-d13a7e6d602b8f84d9177e3138487872f0341d006accfe425886a561f029a9c3R120 and around
val outputDirArgs = outputDir.map(od => Seq("-d", od.toString())).getOrElse(Nil)
val classPathArgs =
if (classPath.nonEmpty)
Seq("-cp", classPath.map(_.toString).mkString(File.pathSeparator))
else Nil
val args = {
val freeArgs = scalacOptions ++ outputDirArgs ++ classPathArgs ++ sources
if (freeArgs.size > Constants.maxScalacArgumentsCount)
argsFilePath.fold(freeArgs) { path =>
os.write(path, freeArgs.mkString(System.lineSeparator()))
Seq(s"@$path")
}
else freeArgs
}
val javaCommand =
javaHomeOpt.map(SimpleJavaCompiler.javaCommand(_)).getOrElse(defaultJavaCommand)
val javaOptions = defaultJavaOptions ++
scalacOptions
.filter(_.startsWith("-J"))
.map(_.stripPrefix("-J")) ++
javacOptions
.filter(_.startsWith("-J"))
.map(_.stripPrefix("-J"))
Runner.runJvm(
javaCommand,
javaOptions,
compilerClassPath,
mainClass,
args,
logger,
cwd = Some(cwd)
).waitFor()
}
/** Run a synthetic (created in runtime) `scalac` as a JVM process for a given
* [[scala.build.Project]]
*
* @param project
* project to be compiled
* @param mainClass
* the main class of the synthetic Scala compiler
* @param outputDir
* the scala compiler output directory
* @param logger
* logger
* @return
* true if the process returned no errors, false otherwise
*/
private def runScalacLikeForProject(
project: Project,
mainClass: String,
outputDir: os.Path,
logger: Logger
): Boolean = {
val res = runScalacLike(
mainClass = mainClass,
javaHomeOpt = project.javaHomeOpt,
javacOptions = project.javacOptions,
scalacOptions = project.scalaCompiler.map(_.scalacOptions).getOrElse(Nil),
classPath = project.classPath,
compilerClassPath = project.scalaCompiler.map(_.compilerClassPath).getOrElse(Nil),
sources = project.sources.map(_.toString),
outputDir = Some(outputDir),
argsFilePath = Some(project.argsFilePath),
cwd = project.workspace,
logger = logger
)
res == 0
}
/** Run a synthetic (created in runtime) `scalac` as a JVM process with minimal parameters. (i.e.
* to print `scalac` help)
*
* @param scalaVersion
* Scala version for which `scalac` is to be created
* @param javaHomeOpt
* Java home path (optional)
* @param javacOptions
* options to be passed for the Java compiler
* @param scalacOptions
* options to be passed for the Scala compiler
* @param fullClassPath
* classpath to be passed to the compiler (optional)
* @param compilerClassPath
* classpath of the compiler itself
* @param logger
* logger
* @return
* compiler process exit code
*/
def runSimpleScalacLike(
scalaVersion: String,
javaHomeOpt: Option[os.Path],
javacOptions: Seq[String],
scalacOptions: Seq[String],
fullClassPath: Seq[os.Path],
compilerClassPath: Seq[os.Path],
logger: Logger
): Int =
compilerMainClass(scalaVersion) match {
case Some(mainClass) =>
runScalacLike(
mainClass = mainClass,
javaHomeOpt = javaHomeOpt,
javacOptions = javacOptions,
scalacOptions = scalacOptions,
classPath = fullClassPath,
compilerClassPath = compilerClassPath,
sources = Nil,
outputDir = None,
argsFilePath = None,
cwd = os.pwd,
logger = logger
)
case _ => 1
}
private def compilerMainClass(scalaVersion: String): Option[String] =
if (scalaVersion.startsWith("2."))
Some {
if (scaladoc) "scala.tools.nsc.ScalaDoc"
else "scala.tools.nsc.Main"
}
else if (scaladoc) None
else Some("dotty.tools.dotc.Main")
def compile(
project: Project,
logger: Logger
): Boolean =
if (project.sources.isEmpty) true
else
project.scalaCompiler match {
case Some(compiler) =>
val isScala2 = compiler.scalaVersion.startsWith("2.")
compilerMainClass(compiler.scalaVersion).forall { mainClass =>
val outputDir =
if (isScala2 && scaladoc) project.scaladocDir
else project.classesDir
runScalacLikeForProject(project, mainClass, outputDir, logger)
}
case None =>
scaladoc ||
SimpleJavaCompiler(defaultJavaCommand, defaultJavaOptions).compile(project, logger)
}
def shutdown(): Unit =
()
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy