package dotty.tools.dotc import dotty.tools.FatalError import config.CompilerCommand import core.Contexts.{Context, ContextBase} import util.DotClass import reporting._ import scala.util.control.NonFatal /** Run the Dotty compiler. * * Extending this class lets you customize many aspect of the compilation * process, but in most cases you only need to call [[process]] on the * existing object [[Main]]. */ abstract class Driver extends DotClass { protected def newCompiler(implicit ctx: Context): Compiler protected def emptyReporter: Reporter = new StoreReporter(null) protected def doCompile(compiler: Compiler, fileNames: List[String])(implicit ctx: Context): Reporter = if (fileNames.nonEmpty) try { val run = compiler.newRun run.compile(fileNames) run.printSummary() } catch { case ex: FatalError => ctx.error(ex.getMessage) // signals that we should fail compilation. ctx.reporter } else ctx.reporter protected def initCtx = (new ContextBase).initialCtx protected def sourcesRequired = true def setup(args: Array[String], rootCtx: Context): (List[String], Context) = { val ctx = rootCtx.fresh val summary = CompilerCommand.distill(args)(ctx) ctx.setSettings(summary.sstate) val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired)(ctx) (fileNames, ctx) } /** Entry point to the compiler that can be conveniently used with Java reflection. * * This entry point can easily be used without depending on the `dotty` package, * you only need to depend on `dotty-interfaces` and call this method using * reflection. This allows you to write code that will work against multiple * versions of dotty without recompilation. * * The trade-off is that you can only pass a SimpleReporter to this method * and not a normal Reporter which is more powerful. * * Usage example: [[https://github.com/lampepfl/dotty/tree/master/test/test/InterfaceEntryPointTest.scala]] * * @param args Arguments to pass to the compiler. * @param simple Used to log errors, warnings, and info messages. * The default reporter is used if this is `null`. * @param callback Used to execute custom code during the compilation * process. No callbacks will be executed if this is `null`. * @return */ final def process(args: Array[String], simple: interfaces.SimpleReporter, callback: interfaces.CompilerCallback): interfaces.ReporterResult = { val reporter = if (simple == null) null else Reporter.fromSimpleReporter(simple) process(args, reporter, callback) } /** Principal entry point to the compiler. * * Usage example: [[https://github.com/lampepfl/dotty/tree/master/test/test/OtherEntryPointsTest.scala]] * in method `runCompiler` * * @param args Arguments to pass to the compiler. * @param reporter Used to log errors, warnings, and info messages. * The default reporter is used if this is `null`. * @param callback Used to execute custom code during the compilation * process. No callbacks will be executed if this is `null`. * @return The `Reporter` used. Use `Reporter#hasErrors` to check * if compilation succeeded. */ final def process(args: Array[String], reporter: Reporter = null, callback: interfaces.CompilerCallback = null): Reporter = { val ctx = initCtx.fresh if (reporter != null) ctx.setReporter(reporter) if (callback != null) ctx.setCompilerCallback(callback) process(args, ctx) } /** Entry point to the compiler with no optional arguments. * * This overload is provided for compatibility reasons: the * `RawCompiler` of sbt expects this method to exist and calls * it using reflection. Keeping it means that we can change * the other overloads without worrying about breaking compatibility * with sbt. */ final def process(args: Array[String]): Reporter = process(args, null: Reporter, null: interfaces.CompilerCallback) /** Entry point to the compiler using a custom `Context`. * * In most cases, you do not need a custom `Context` and should * instead use one of the other overloads of `process`. However, * the other overloads cannot be overriden, instead you * should override this one which they call internally. * * Usage example: [[https://github.com/lampepfl/dotty/tree/master/test/test/OtherEntryPointsTest.scala]] * in method `runCompilerWithContext` * * @param args Arguments to pass to the compiler. * @param rootCtx The root Context to use. * @return The `Reporter` used. Use `Reporter#hasErrors` to check * if compilation succeeded. */ def process(args: Array[String], rootCtx: Context): Reporter = { val (fileNames, ctx) = setup(args, rootCtx) doCompile(newCompiler(ctx), fileNames)(ctx) } def main(args: Array[String]): Unit = { // Preload scala.util.control.NonFatal. Otherwise, when trying to catch a StackOverflowError, // we may try to load it but fail with another StackOverflowError and lose the original exception, // see . val _ = NonFatal sys.exit(if (process(args).hasErrors) 1 else 0) } }