From ccf7f8ae4e09c1e5cbbc52d64ff0358203d29d5c Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Wed, 2 Dec 2015 16:26:12 +0100 Subject: Add initial CompilerCallback implementation for IntelliJ This adds some simple callbacks to Dotty that should be enough to get basic integration from IntelliJ. --- src/dotty/tools/backend/jvm/GenBCode.scala | 11 ++++++++ src/dotty/tools/dotc/CompilerCallback.scala | 42 +++++++++++++++++++++++++++++ src/dotty/tools/dotc/Driver.scala | 9 ++++++- src/dotty/tools/dotc/core/Contexts.scala | 15 +++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 src/dotty/tools/dotc/CompilerCallback.scala diff --git a/src/dotty/tools/backend/jvm/GenBCode.scala b/src/dotty/tools/backend/jvm/GenBCode.scala index feaae036b..e8d196ce7 100644 --- a/src/dotty/tools/backend/jvm/GenBCode.scala +++ b/src/dotty/tools/backend/jvm/GenBCode.scala @@ -23,6 +23,7 @@ import Symbols._ import Denotations._ import Phases._ import java.lang.AssertionError +import java.io.{ File => JFile } import scala.tools.asm import scala.tools.asm.tree._ import dotty.tools.dotc.util.{Positions, DotClass} @@ -47,6 +48,8 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter var tree: Tree = _ + val sourceJFile: JFile = ctx.compilationUnit.source.file.file + final class PlainClassBuilder(cunit: CompilationUnit) extends SyncAndTryBuilder(cunit) @@ -300,6 +303,9 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter bytecodeWriter.close() // Statistics.stopTimer(BackendStats.bcodeTimer, bcodeStart) + if (ctx.compilerCallback != null) + ctx.compilerCallback.onSourceCompiled(sourceJFile) + /* TODO Bytecode can be verified (now that all classfiles have been written to disk) * * (1) asm.util.CheckAdapter.verify() @@ -363,6 +369,11 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter if (outFolder == null) null else getFileForClassfile(outFolder, jclassName, ".class") bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, outFile) + + val outJFile = outFile.file + val className = jclassName.replace('/', '.') + if (ctx.compilerCallback != null) + ctx.compilerCallback.onClassGenerated(sourceJFile, outJFile, className) } catch { case e: FileConflictException => diff --git a/src/dotty/tools/dotc/CompilerCallback.scala b/src/dotty/tools/dotc/CompilerCallback.scala new file mode 100644 index 000000000..98e8e2713 --- /dev/null +++ b/src/dotty/tools/dotc/CompilerCallback.scala @@ -0,0 +1,42 @@ +package dotty.tools.dotc + +import java.io.File + +/** This trait contains methods that can be overriden to execute code during the + * compilation process. + * + * NOTE: This trait is experimental and may be subject to arbitrary changes. + * + * Example usage: + * {{{ + * val args: Array[String] = ... + * val callback = new CompilerCallback { + * override def onClassGenerated(source: File, generatedClass: File, className: String) = + * println(s"onClassGenerated($source, $generatedClass, $className)") + * override def onSourceCompiled(source: File) = + * println(s"onSourceCompiled($source)") + * } + * dotty.tools.dotc.process(args, callback) + * // Or, if you have a custom root context `rootCtx`: + * dotty.tools.dotc.process(args, rootCtx.setCompilerCallback(callback)) + * }}} + */ +trait CompilerCallback { + /** Called when a class has been generated. + * + * @param source The source file corresponding to this class. + * Example: ./src/library/scala/collection/Seq.scala + * @param generatedClass The generated classfile for this class. + * Example: ./scala/collection/Seq$.class + * @param className The name of this class. + * Example: scala.collection.Seq$ + */ + def onClassGenerated(source: File, generatedClass: File, className: String): Unit = {} + + /** Called when every class for this file has been generated. + * + * @param source The source file. + * Example: ./src/library/scala/collection/Seq.scala + */ + def onSourceCompiled(source: File): Unit = {} +} diff --git a/src/dotty/tools/dotc/Driver.scala b/src/dotty/tools/dotc/Driver.scala index 22170a478..1627b6e48 100644 --- a/src/dotty/tools/dotc/Driver.scala +++ b/src/dotty/tools/dotc/Driver.scala @@ -34,7 +34,10 @@ abstract class Driver extends DotClass { def setup(args: Array[String], rootCtx: Context): (List[String], Context) = { val summary = CompilerCommand.distill(args)(rootCtx) - implicit val ctx: Context = initCtx.fresh.setSettings(summary.sstate) + // FIXME: We should reuse rootCtx instead of creating newCtx, but this + // makes some tests fail with "denotation module _root_ invalid in run 2." + val newCtx = initCtx.setCompilerCallback(rootCtx.compilerCallback) + implicit val ctx: Context = newCtx.fresh.setSettings(summary.sstate) val fileNames = CompilerCommand.checkUsage(summary, sourcesRequired) (fileNames, ctx) } @@ -44,6 +47,10 @@ abstract class Driver extends DotClass { doCompile(newCompiler(), fileNames)(ctx) } + def process(args: Array[String], callback: CompilerCallback): Reporter = { + process(args, initCtx.setCompilerCallback(callback)) + } + // We overload `process` instead of using a default argument so that we // can easily call this method using reflection from `RawCompiler` in sbt. def process(args: Array[String]): Reporter = { diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 522240b0f..1a471537e 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -72,11 +72,23 @@ object Contexts { def next = { val c = current; current = current.outer; c } } + /** Set the compiler callback, shared by all contexts with the same `base` */ + def setCompilerCallback(callback: CompilerCallback): this.type = { + base.compilerCallback = callback; this + } + /** The outer context */ private[this] var _outer: Context = _ protected def outer_=(outer: Context) = _outer = outer def outer: Context = _outer + // protected def compilerCallback_=(callback: CompilerCallback) = + // _compilerCallback = callback + // def compilerCallback: CompilerCallback = _compilerCallback + // def setCompilerCallback(callback: CompilerCallback): this.type = { + // this.compilerCallback = callback; this + // } + /** The current context */ private[this] var _period: Period = _ protected def period_=(period: Period) = { @@ -525,6 +537,9 @@ object Contexts { /** The essential mutable state of a context base, collected into a common class */ class ContextState { + /** The compiler callback implementation, or null if unset */ + var compilerCallback: CompilerCallback = _ + // Symbols state /** A counter for unique ids */ -- cgit v1.2.3