aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Martres <smarter@ubuntu.com>2016-02-24 23:53:35 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-02-28 20:08:59 +0100
commit7e7ee820df7647680d9aaf1ca991fe9718159097 (patch)
tree26095cec3a83fc12984e745e459dcb3d0996d045
parent94b41d5c491878543288af1bedb4daf57226ca07 (diff)
downloaddotty-7e7ee820df7647680d9aaf1ca991fe9718159097.tar.gz
dotty-7e7ee820df7647680d9aaf1ca991fe9718159097.tar.bz2
dotty-7e7ee820df7647680d9aaf1ca991fe9718159097.zip
Add a `dotty-interfaces` package
We introduce a new entry point for the compiler in `dotty.tools.dotc.Driver`: ``` def process(args: Array[String], simple: interfaces.SimpleReporter, callback: interfaces.CompilerCallback): interfaces.ReporterResult ``` Except for `args` which is just an array, the argument types and return type of this method are Java interfaces defined in a new package called `dotty-interfaces` which has a stable ABI. This means that you can programmatically run a compiler with a custom reporter and callbacks without having to recompile it against every version of dotty: you only need to have `dotty-interfaces` present at compile-time and call the `process` method using Java reflection. See `test/test/InterfaceEntryPointTest.scala` for a concrete example. This design is based on discussions with the IntelliJ IDEA Scala plugin team. Thanks to Nikolay Tropin for the discussions and his PR proposal (see #1011).
-rwxr-xr-xbin/dotc18
-rw-r--r--interfaces/src/main/java/dotty/tools/dotc/interfaces/AbstractFile.java24
-rw-r--r--interfaces/src/main/java/dotty/tools/dotc/interfaces/CompilerCallback.java28
-rw-r--r--interfaces/src/main/java/dotty/tools/dotc/interfaces/Diagnostic.java26
-rw-r--r--interfaces/src/main/java/dotty/tools/dotc/interfaces/ReporterResult.java18
-rw-r--r--interfaces/src/main/java/dotty/tools/dotc/interfaces/SimpleReporter.java13
-rw-r--r--interfaces/src/main/java/dotty/tools/dotc/interfaces/SourceFile.java13
-rw-r--r--interfaces/src/main/java/dotty/tools/dotc/interfaces/SourcePosition.java44
-rw-r--r--project/Build.scala11
-rw-r--r--src/dotty/tools/backend/jvm/GenBCode.scala20
-rw-r--r--src/dotty/tools/dotc/CompilerCallback.scala42
-rw-r--r--src/dotty/tools/dotc/Driver.scala42
-rw-r--r--src/dotty/tools/dotc/core/Contexts.scala1
-rw-r--r--src/dotty/tools/dotc/reporting/Diagnostic.scala14
-rw-r--r--src/dotty/tools/dotc/reporting/Reporter.scala14
-rw-r--r--src/dotty/tools/dotc/util/SourceFile.scala8
-rw-r--r--src/dotty/tools/dotc/util/SourcePosition.scala5
-rw-r--r--test/test/InterfaceEntryPointTest.scala61
-rw-r--r--test/test/OtherEntryPointsTest.scala66
19 files changed, 397 insertions, 71 deletions
diff --git a/bin/dotc b/bin/dotc
index 623a60eef..61e84a19d 100755
--- a/bin/dotc
+++ b/bin/dotc
@@ -42,7 +42,8 @@ ReplMain=test.DottyRepl
-# autodetecting the compiler jar. this is location where sbt 'packages' it
+# autodetecting the compiler jars. this is the location where sbt 'packages' them
+INTERFACES_JAR=$DOTTY_ROOT/interfaces/target/dotty-interfaces-$DOTTY_VERSION.jar
MAIN_JAR=$DOTTY_ROOT/target/scala-$SCALA_BINARY_VERSION/dotty_$SCALA_BINARY_VERSION-$DOTTY_VERSION.jar
TEST_JAR=$DOTTY_ROOT/target/scala-$SCALA_BINARY_VERSION/dotty_$SCALA_BINARY_VERSION-$DOTTY_VERSION-tests.jar
DOTTY_JAR=$DOTTY_ROOT/dotty.jar
@@ -62,7 +63,7 @@ function checkjar {
echo "The required jar file was built successfully."
fi
else
- NEW_FILES="$(find "$DOTTY_ROOT/$3" -iname "*.scala" -newer "$1")"
+ NEW_FILES="$(find "$DOTTY_ROOT/$3" \( -iname "*.scala" -o -iname "*.java" \) -newer "$1")"
if [ ! -z "$NEW_FILES" ];
then
echo "new files detected. rebuilding"
@@ -74,6 +75,7 @@ function checkjar {
fi
}
+checkjar $INTERFACES_JAR interfaces/package interfaces
checkjar $MAIN_JAR package src
checkjar $TEST_JAR test:package test
@@ -198,6 +200,8 @@ classpathArgs () {
else
toolchain="$SCALA_LIBRARY_JAR:$SCALA_REFLECT_JAR:$SCALA_COMPILER_JAR:$JLINE_JAR"
fi
+ bcpJars="$INTERFACES_JAR:$MAIN_JAR"
+ cpJars="$INTERFACES_JAR:$MAIN_JAR:$TEST_JAR"
if [[ -n "$cygwin" ]]; then
if [[ "$OS" = "Windows_NT" ]] && cygpath -m .>/dev/null 2>/dev/null ; then
@@ -207,18 +211,18 @@ classpathArgs () {
fi
if [[ -n $bootcp ]]; then
- boot_classpath="$(cygpath --path --$format "$toolchain:$MAIN_JAR")"
- classpath="$(cygpath --path --$format "$MAIN_JAR:$TEST_JAR")"
+ boot_classpath="$(cygpath --path --$format "$toolchain:$bcpJars")"
+ classpath="$(cygpath --path --$format "$cpJars")"
cpArgs="-Xbootclasspath/a:$boot_classpath -classpath $classpath"
else
- classpath="$(cygpath --path --$format "$toolchain:$MAIN_JAR:$TEST_JAR")"
+ classpath="$(cygpath --path --$format "$toolchain:$cpJars")"
cpArgs="-classpath $classpath"
fi
else
if [[ -n $bootcp ]]; then
- cpArgs="-Xbootclasspath/a:$toolchain:$MAIN_JAR -classpath $MAIN_JAR:$TEST_JAR"
+ cpArgs="-Xbootclasspath/a:$toolchain:$bcpJars -classpath $cpJars"
else
- cpArgs="-classpath $toolchain:$MAIN_JAR:$TEST_JAR"
+ cpArgs="-classpath $toolchain:$cpJars"
fi
fi
echo ${cpArgs}
diff --git a/interfaces/src/main/java/dotty/tools/dotc/interfaces/AbstractFile.java b/interfaces/src/main/java/dotty/tools/dotc/interfaces/AbstractFile.java
new file mode 100644
index 000000000..a1b9cc5c7
--- /dev/null
+++ b/interfaces/src/main/java/dotty/tools/dotc/interfaces/AbstractFile.java
@@ -0,0 +1,24 @@
+package dotty.tools.dotc.interfaces;
+
+import java.io.File;
+import java.util.Optional;
+
+/** An abstract file may either be a file on disk or a virtual file.
+ *
+ * Do not rely on the identity of instances of this class.
+ *
+ * User code should not implement this interface, but it may have to
+ * manipulate objects of this type.
+ */
+public interface AbstractFile {
+ /** The name of this file, note that two files may have the same name. */
+ String name();
+
+ /** The path of this file, this might be a virtual path of an unspecified format. */
+ String path();
+
+ /** If this is a real file on disk, a `java.io.File` that corresponds to this file.
+ * Otherwise, an empty `Optional`.
+ */
+ Optional<File> jfile();
+}
diff --git a/interfaces/src/main/java/dotty/tools/dotc/interfaces/CompilerCallback.java b/interfaces/src/main/java/dotty/tools/dotc/interfaces/CompilerCallback.java
new file mode 100644
index 000000000..17d2f6bbd
--- /dev/null
+++ b/interfaces/src/main/java/dotty/tools/dotc/interfaces/CompilerCallback.java
@@ -0,0 +1,28 @@
+package dotty.tools.dotc.interfaces;
+
+/** Set of callbacks called in response to events during the compilation process.
+ *
+ * You should implement this interface if you want to react to one or more of
+ * these events.
+ *
+ * @see the method `process` of `dotty.tools.dotc.Driver` for more information.
+ */
+public interface 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$
+ */
+ default void onClassGenerated(SourceFile source, AbstractFile generatedClass, String className) {};
+
+ /** Called when every class for this file has been generated.
+ *
+ * @param source The source file.
+ * Example: ./src/library/scala/collection/Seq.scala
+ */
+ default void onSourceCompiled(SourceFile source) {};
+}
diff --git a/interfaces/src/main/java/dotty/tools/dotc/interfaces/Diagnostic.java b/interfaces/src/main/java/dotty/tools/dotc/interfaces/Diagnostic.java
new file mode 100644
index 000000000..436f9f8ea
--- /dev/null
+++ b/interfaces/src/main/java/dotty/tools/dotc/interfaces/Diagnostic.java
@@ -0,0 +1,26 @@
+package dotty.tools.dotc.interfaces;
+
+import java.util.Optional;
+
+/** A diagnostic is a message emitted during the compilation process.
+ *
+ * It can either be an error, a warning or an information.
+ *
+ * User code should not implement this interface, but it may have to
+ * manipulate objects of this type.
+ */
+public interface Diagnostic {
+ public static final int ERROR = 2;
+ public static final int WARNING = 1;
+ public static final int INFO = 0;
+
+ /** The message to report */
+ String message();
+
+ /** Level of the diagnostic, can be either ERROR, WARNING or INFO */
+ int level();
+
+ /** The position in a source file of the code that caused this diagnostic
+ * to be emitted. */
+ Optional<SourcePosition> position();
+}
diff --git a/interfaces/src/main/java/dotty/tools/dotc/interfaces/ReporterResult.java b/interfaces/src/main/java/dotty/tools/dotc/interfaces/ReporterResult.java
new file mode 100644
index 000000000..63c5104df
--- /dev/null
+++ b/interfaces/src/main/java/dotty/tools/dotc/interfaces/ReporterResult.java
@@ -0,0 +1,18 @@
+package dotty.tools.dotc.interfaces;
+
+/** Summary of the diagnostics emitted by a Reporter.
+ *
+ * User code should not implement this interface, but it may have to
+ * manipulate objects of this type.
+ */
+public interface ReporterResult {
+ /** Have we emitted any error ? */
+ boolean hasErrors();
+ /** Number of errors that have been emitted */
+ int errorCount();
+
+ /** Have we emitted any warning ? */
+ boolean hasWarnings();
+ /** Number of warnings that have been emitted */
+ int warningCount();
+}
diff --git a/interfaces/src/main/java/dotty/tools/dotc/interfaces/SimpleReporter.java b/interfaces/src/main/java/dotty/tools/dotc/interfaces/SimpleReporter.java
new file mode 100644
index 000000000..e52537096
--- /dev/null
+++ b/interfaces/src/main/java/dotty/tools/dotc/interfaces/SimpleReporter.java
@@ -0,0 +1,13 @@
+package dotty.tools.dotc.interfaces;
+
+/** Report errors, warnings and info messages during the compilation process
+ *
+ * You should implement this interface if you want to handle the diagnostics
+ * returned by the compiler yourself.
+ *
+ * @see the method `process` of `dotty.tools.dotc.Driver` for more information.
+ */
+public interface SimpleReporter {
+ /** Report a diagnostic. */
+ void report(Diagnostic diag);
+}
diff --git a/interfaces/src/main/java/dotty/tools/dotc/interfaces/SourceFile.java b/interfaces/src/main/java/dotty/tools/dotc/interfaces/SourceFile.java
new file mode 100644
index 000000000..5611d3885
--- /dev/null
+++ b/interfaces/src/main/java/dotty/tools/dotc/interfaces/SourceFile.java
@@ -0,0 +1,13 @@
+package dotty.tools.dotc.interfaces;
+
+import java.io.File;
+
+/** A source file.
+ *
+ * User code should not implement this interface, but it may have to
+ * manipulate objects of this type.
+ */
+public interface SourceFile extends AbstractFile {
+ /** The content of this file as seen by the compiler. */
+ char[] content();
+}
diff --git a/interfaces/src/main/java/dotty/tools/dotc/interfaces/SourcePosition.java b/interfaces/src/main/java/dotty/tools/dotc/interfaces/SourcePosition.java
new file mode 100644
index 000000000..c9b42d4ad
--- /dev/null
+++ b/interfaces/src/main/java/dotty/tools/dotc/interfaces/SourcePosition.java
@@ -0,0 +1,44 @@
+package dotty.tools.dotc.interfaces;
+
+/** A position in a source file.
+ *
+ * A position is a range between a start offset and an end offset, as well as a
+ * point inside this range.
+ *
+ * As a convenience, we also provide methods that return the line and the column
+ * corresponding to each offset.
+ *
+ * User code should not implement this interface, but it may have to
+ * manipulate objects of this type.
+ */
+public interface SourcePosition {
+ /** Content of the line which contains the point */
+ String lineContent();
+
+ /** Offset to the point */
+ int point();
+ /** Line number of the point, starting at 0 */
+ int line();
+ /** Column number of the point, starting at 0 */
+ int column();
+
+ /** Offset to the range start */
+ int start();
+ /** Line number of the range start, starting at 0 */
+ int startLine();
+ /** Column number of the range start, starting at 0 */
+ int startColumn();
+
+ /** Offset to the range end */
+ int end();
+ /** Line number of the range end, starting at 0 */
+ int endLine();
+ /** Column number of the range end, starting at 0 */
+ int endColumn();
+
+ /** The source file corresponding to this position.
+ * The values returned by `point()`, `start()` and `end()`
+ * are indices in the array returned by `source().content()`.
+ */
+ SourceFile source();
+}
diff --git a/project/Build.scala b/project/Build.scala
index 980c7ccb0..8b7238a09 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -35,7 +35,16 @@ object DottyBuild extends Build {
)
}
+ lazy val `dotty-interfaces` = project.in(file("interfaces")).
+ settings(
+ // Do not append Scala versions to the generated artifacts
+ crossPaths := false,
+ // Do not depend on the Scala library
+ autoScalaLibrary := false
+ )
+
lazy val dotty = project.in(file(".")).
+ dependsOn(`dotty-interfaces`).
settings(
// set sources to src/, tests to test/ and resources to resources/
scalaSource in Compile := baseDirectory.value / "src",
@@ -105,7 +114,7 @@ object DottyBuild extends Build {
parallelExecution in Test := false,
// http://grokbase.com/t/gg/simple-build-tool/135ke5y90p/sbt-setting-jvm-boot-paramaters-for-scala
- javaOptions <++= (managedClasspath in Runtime, packageBin in Compile) map { (attList, bin) =>
+ javaOptions <++= (dependencyClasspath in Runtime, packageBin in Compile) map { (attList, bin) =>
// put the Scala {library, reflect} in the classpath
val path = for {
file <- attList.map(_.data)
diff --git a/src/dotty/tools/backend/jvm/GenBCode.scala b/src/dotty/tools/backend/jvm/GenBCode.scala
index 2d444d3be..8cec93977 100644
--- a/src/dotty/tools/backend/jvm/GenBCode.scala
+++ b/src/dotty/tools/backend/jvm/GenBCode.scala
@@ -13,6 +13,9 @@ import dotty.tools.dotc
import dotty.tools.dotc.backend.jvm.DottyPrimitives
import dotty.tools.dotc.transform.Erasure
+import dotty.tools.dotc.interfaces
+import java.util.Optional
+
import scala.reflect.ClassTag
import dotty.tools.dotc.core._
import Periods._
@@ -51,7 +54,17 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
var tree: Tree = _
- val sourceJFile: JFile = ctx.compilationUnit.source.file.file
+ val sourceFile = ctx.compilationUnit.source
+
+ /** Convert a `scala.reflect.io.AbstractFile` into a
+ * `dotty.tools.dotc.interfaces.AbstractFile`.
+ */
+ private[this] def convertAbstractFile(absfile: scala.reflect.io.AbstractFile): interfaces.AbstractFile =
+ new interfaces.AbstractFile {
+ override def name = absfile.name
+ override def path = absfile.path
+ override def jfile = Optional.ofNullable(absfile.file)
+ }
final class PlainClassBuilder(cunit: CompilationUnit) extends SyncAndTryBuilder(cunit)
@@ -307,7 +320,7 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
// Statistics.stopTimer(BackendStats.bcodeTimer, bcodeStart)
if (ctx.compilerCallback != null)
- ctx.compilerCallback.onSourceCompiled(sourceJFile)
+ ctx.compilerCallback.onSourceCompiled(sourceFile)
/* TODO Bytecode can be verified (now that all classfiles have been written to disk)
*
@@ -373,10 +386,9 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
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)
+ ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(outFile), className)
}
catch {
case e: FileConflictException =>
diff --git a/src/dotty/tools/dotc/CompilerCallback.scala b/src/dotty/tools/dotc/CompilerCallback.scala
deleted file mode 100644
index e2f56430b..000000000
--- a/src/dotty/tools/dotc/CompilerCallback.scala
+++ /dev/null
@@ -1,42 +0,0 @@
-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.fresh.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 5f613d67d..887274fa8 100644
--- a/src/dotty/tools/dotc/Driver.scala
+++ b/src/dotty/tools/dotc/Driver.scala
@@ -7,6 +7,12 @@ 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 {
val prompt = "\ndotc> "
@@ -41,12 +47,35 @@ abstract class Driver extends DotClass {
(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.
- * Creates a new compiler instance and run it with arguments `args`.
*
- * The optional arguments of this method all have `null` as their default
- * value, this makes it easier to call this method by reflection or from Java.
+ * 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.
@@ -57,7 +86,7 @@ abstract class Driver extends DotClass {
* if compilation succeeded.
*/
final def process(args: Array[String], reporter: Reporter = null,
- callback: CompilerCallback = null): Reporter = {
+ callback: interfaces.CompilerCallback = null): Reporter = {
val ctx = initCtx.fresh
if (reporter != null)
ctx.setReporter(reporter)
@@ -75,7 +104,7 @@ abstract class Driver extends DotClass {
* with sbt.
*/
final def process(args: Array[String]): Reporter =
- process(args, null, null)
+ process(args, null: Reporter, null: interfaces.CompilerCallback)
/** Entry point to the compiler using a custom `Context`.
*
@@ -84,6 +113,9 @@ abstract class Driver extends DotClass {
* 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
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala
index c1132ce4b..f0537dffa 100644
--- a/src/dotty/tools/dotc/core/Contexts.scala
+++ b/src/dotty/tools/dotc/core/Contexts.scala
@@ -2,6 +2,7 @@ package dotty.tools
package dotc
package core
+import interfaces.CompilerCallback
import Decorators._
import Periods._
import Names._
diff --git a/src/dotty/tools/dotc/reporting/Diagnostic.scala b/src/dotty/tools/dotc/reporting/Diagnostic.scala
index ea3ea4112..bcf55e993 100644
--- a/src/dotty/tools/dotc/reporting/Diagnostic.scala
+++ b/src/dotty/tools/dotc/reporting/Diagnostic.scala
@@ -4,22 +4,22 @@ package reporting
import util.SourcePosition
-object Diagnostic {
-
- // Error levels
- val ERROR = 2
- val WARNING = 1
- val INFO = 0
+import java.util.Optional
+object Diagnostic {
val nonSensicalStartTag = "<nonsensical>"
val nonSensicalEndTag = "</nonsensical>"
}
-class Diagnostic(msgFn: => String, val pos: SourcePosition, val level: Int) extends Exception {
+class Diagnostic(msgFn: => String, val pos: SourcePosition, val level: Int)
+ extends Exception with interfaces.Diagnostic {
import Diagnostic._
private var myMsg: String = null
private var myIsNonSensical: Boolean = false
+ override def position: Optional[interfaces.SourcePosition] =
+ if (pos.exists && pos.source.exists) Optional.of(pos) else Optional.empty()
+
/** The message to report */
def message: String = {
if (myMsg == null) {
diff --git a/src/dotty/tools/dotc/reporting/Reporter.scala b/src/dotty/tools/dotc/reporting/Reporter.scala
index f4eb551a1..8236f93ef 100644
--- a/src/dotty/tools/dotc/reporting/Reporter.scala
+++ b/src/dotty/tools/dotc/reporting/Reporter.scala
@@ -11,7 +11,7 @@ import config.Settings.Setting
import config.Printers
import java.lang.System.currentTimeMillis
import typer.Mode
-import Diagnostic.{ERROR, WARNING, INFO}
+import interfaces.Diagnostic.{ERROR, WARNING, INFO}
object Reporter {
class Error(msgFn: => String, pos: SourcePosition) extends Diagnostic(msgFn, pos, ERROR)
@@ -33,6 +33,16 @@ object Reporter {
class MigrationWarning(msgFn: => String, pos: SourcePosition) extends ConditionalWarning(msgFn, pos) {
def enablingOption(implicit ctx: Context) = ctx.settings.migration
}
+
+ /** Convert a SimpleReporter into a real Reporter */
+ def fromSimpleReporter(simple: interfaces.SimpleReporter): Reporter =
+ new Reporter with UniqueMessagePositions with HideNonSensicalMessages {
+ override def doReport(d: Diagnostic)(implicit ctx: Context): Unit = d match {
+ case d: ConditionalWarning if !d.enablingOption.value =>
+ case _ =>
+ simple.report(d)
+ }
+ }
}
import Reporter._
@@ -157,7 +167,7 @@ trait Reporting { this: Context =>
* This interface provides methods to issue information, warning and
* error messages.
*/
-abstract class Reporter {
+abstract class Reporter extends interfaces.ReporterResult {
/** Report a diagnostic */
def doReport(d: Diagnostic)(implicit ctx: Context): Unit
diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala
index ac3ee298e..6b547203e 100644
--- a/src/dotty/tools/dotc/util/SourceFile.scala
+++ b/src/dotty/tools/dotc/util/SourceFile.scala
@@ -11,6 +11,8 @@ import Chars._
import ScriptSourceFile._
import Positions._
+import java.util.Optional
+
object ScriptSourceFile {
@sharable private val headerPattern = Pattern.compile("""^(::)?!#.*(\r|\n|\r\n)""", Pattern.MULTILINE)
private val headerStarts = List("#!", "::#!")
@@ -32,7 +34,7 @@ object ScriptSourceFile {
}
}
-case class SourceFile(file: AbstractFile, content: Array[Char]) {
+case class SourceFile(file: AbstractFile, content: Array[Char]) extends interfaces.SourceFile {
def this(_file: AbstractFile) = this(_file, _file.toCharArray)
def this(sourceName: String, cs: Seq[Char]) = this(new VirtualFile(sourceName), cs.toArray)
@@ -41,6 +43,10 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) {
/** Tab increment; can be overridden */
def tabInc = 8
+ override def name = file.name
+ override def path = file.path
+ override def jfile = Optional.ofNullable(file.file)
+
override def equals(that : Any) = that match {
case that : SourceFile => file.path == that.file.path && start == that.start
case _ => false
diff --git a/src/dotty/tools/dotc/util/SourcePosition.scala b/src/dotty/tools/dotc/util/SourcePosition.scala
index fa7a4650d..0b2b2aa0b 100644
--- a/src/dotty/tools/dotc/util/SourcePosition.scala
+++ b/src/dotty/tools/dotc/util/SourcePosition.scala
@@ -1,10 +1,11 @@
package dotty.tools
-package dotc.util
+package dotc
+package util
import Positions.{Position, NoPosition}
/** A source position is comprised of a position in a source file */
-case class SourcePosition(source: SourceFile, pos: Position) {
+case class SourcePosition(source: SourceFile, pos: Position) extends interfaces.SourcePosition {
def exists = pos.exists
def lineContent: String = source.lineContent(point)
diff --git a/test/test/InterfaceEntryPointTest.scala b/test/test/InterfaceEntryPointTest.scala
new file mode 100644
index 000000000..b193b5f64
--- /dev/null
+++ b/test/test/InterfaceEntryPointTest.scala
@@ -0,0 +1,61 @@
+package test
+
+import org.junit.Test
+import org.junit.Assert._
+import dotty.tools.dotc.interfaces._
+import scala.collection.mutable.ListBuffer
+
+/** Test that demonstrates how to use dotty-interfaces
+ *
+ * This test requires:
+ * - dotty-interfaces to be present at compile-time
+ * - dotty-interfaces and dotty to be present at run-time
+ *
+ * Since the ABI of dotty-interfaces is stable, this means that users can write
+ * code that works with multiple versions of dotty without recompilation.
+ *
+ * @see [[OtherEntryPointsTest]]
+ */
+class InterfaceEntryPointTest {
+ @Test def runCompilerFromInterface = {
+ val sources = List("./tests/pos/HelloWorld.scala")
+ val args = sources ++ List("-d", "./out/")
+
+ val mainClass = Class.forName("dotty.tools.dotc.Main")
+ val process = mainClass.getMethod("process",
+ classOf[Array[String]], classOf[SimpleReporter], classOf[CompilerCallback])
+
+ val reporter = new CustomSimpleReporter
+ val callback = new CustomCompilerCallback
+
+ // Run the compiler by calling dotty.tools.dotc.Main.process
+ process.invoke(null, args.toArray, reporter, callback)
+
+ assertEquals("Number of errors", 0, reporter.errorCount)
+ assertEquals("Number of warnings", 0, reporter.warningCount)
+ assertEquals("Compiled sources", sources, callback.paths)
+ }
+
+ private class CustomSimpleReporter extends SimpleReporter {
+ var errorCount = 0
+ var warningCount = 0
+
+ def report(diag: Diagnostic): Unit = {
+ if (diag.level == Diagnostic.ERROR)
+ errorCount += 1
+ if (diag.level == Diagnostic.WARNING)
+ warningCount += 1
+ }
+ }
+
+ private class CustomCompilerCallback extends CompilerCallback {
+ private val pathsBuffer = new ListBuffer[String]
+ def paths = pathsBuffer.toList
+
+ override def onSourceCompiled(source: SourceFile): Unit = {
+ if (source.jfile.isPresent)
+ pathsBuffer += source.jfile.get.getPath
+ }
+ }
+}
+
diff --git a/test/test/OtherEntryPointsTest.scala b/test/test/OtherEntryPointsTest.scala
new file mode 100644
index 000000000..0186b357b
--- /dev/null
+++ b/test/test/OtherEntryPointsTest.scala
@@ -0,0 +1,66 @@
+package test
+
+import org.junit.Test
+import org.junit.Assert._
+import dotty.tools.dotc.Main
+import dotty.tools.dotc.interfaces.{CompilerCallback, SourceFile}
+import dotty.tools.dotc.reporting._
+import dotty.tools.dotc.core.Contexts._
+import java.io.File
+import scala.collection.mutable.ListBuffer
+
+/** Test the compiler entry points that depend on dotty
+ *
+ * This file also serve as an example for using [[dotty.tools.dotc.Driver#process]].
+ *
+ * @see [[InterfaceEntryPointTest]]
+ */
+class OtherEntryPointsTest {
+ @Test def runCompiler = {
+ val sources = List("./tests/pos/HelloWorld.scala")
+ val args = sources ++ List("-d", "./out/")
+
+ val reporter = new CustomReporter
+ val callback = new CustomCompilerCallback
+
+ Main.process(args.toArray, reporter, callback)
+
+ assertEquals("Number of errors", false, reporter.hasErrors)
+ assertEquals("Number of warnings", false, reporter.hasWarnings)
+ assertEquals("Compiled sources", sources, callback.paths)
+ }
+
+ @Test def runCompilerWithContext = {
+ val sources = List("./tests/pos/HelloWorld.scala")
+ val args = sources ++ List("-d", "./out/")
+
+ val reporter = new CustomReporter
+ val callback = new CustomCompilerCallback
+ val context = (new ContextBase).initialCtx.fresh
+ .setReporter(reporter)
+ .setCompilerCallback(callback)
+
+ Main.process(args.toArray, context)
+
+ assertEquals("Number of errors", false, reporter.hasErrors)
+ assertEquals("Number of warnings", false, reporter.hasWarnings)
+ assertEquals("Compiled sources", sources, callback.paths)
+ }
+
+ private class CustomReporter extends Reporter
+ with UniqueMessagePositions
+ with HideNonSensicalMessages {
+ def doReport(d: Diagnostic)(implicit ctx: Context): Unit = {
+ }
+ }
+
+ private class CustomCompilerCallback extends CompilerCallback {
+ private val pathsBuffer = new ListBuffer[String]
+ def paths = pathsBuffer.toList
+
+ override def onSourceCompiled(source: SourceFile): Unit = {
+ if (source.jfile.isPresent)
+ pathsBuffer += source.jfile.get.getPath
+ }
+ }
+}