Skip to content

Commit 341e76c

Browse files
committed
Add sbt incremental compilation support via a special phase
To test this with sbt, see https://github.com/lampepfl/dotty/wiki/Using-Dotty-with-sbt The following flags affect this phase: - -Yforce-sbt-phase: Run this compiler phase even if the compiler is ran outside of sbt, for debugging. - -Ydump-sbt-api: For every compiled foo.scala, output the sbt API representation in foo.api, implies -Yforce-sbt-phase. This phase is split in two parts: - `ExtractAPI` which creates a representation of the API of the current compilation unit. - `ExtractDependencies` which extracts the dependency information of the current compilation unit. Briefly, when a file changes sbt will recompile it, if its API has changed then sbt will determine which reverse-dependency of the file have to be recompiled depending on what changed and what the reverse-dependencies use. See http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html for more information on how sbt incremental compilation works. This phase was originally based on https://github.com/adriaanm/scala/tree/sbt-api-consolidate/src/compiler/scala/tools/sbt which attempts to integrate the sbt phases into scalac (and is itself based on https://github.com/sbt/sbt/tree/0.13/compile/interface/src/main/scala/xsbt), but it has been heavily refactored and adapted to Dotty. The main functional differences are: - This phase runs right after the typer, before PostTyper which gets rid of important information for incremental compilation (it simplifies trees that have a constant type and trees that represent types). - `ExtractAPI` only extract types as they are defined and never "as seen from" some some specific prefix, see its documentation for more details. - `ExtractDependenciesTraverser` and `ExtractUsedNames` have been fused into one tree traversal. TODO: Try to run this phase in parallel with the rest of the compiler pipeline since they're independent (except for the sbt callbacks in `GenBCode`) ?
1 parent aca6a9c commit 341e76c

File tree

12 files changed

+962
-11
lines changed

12 files changed

+962
-11
lines changed

AUTHORS.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,15 @@ The majority of the dotty codebase is new code, with the exception of the compon
4242
> The utilities package is a mix of new and adapted components. The files in [scala/scala](https://github.com/scala/scala) were originally authored by many people,
4343
> including Paul Phillips, Martin Odersky, Sean McDirmid, and others.
4444
45-
`dotty.tools.io`
45+
`dotty.tools.io`
4646

4747
> The I/O support library was adapted from current Scala compiler. Original authors were Paul Phillips and others.
48+
49+
`dotty.tools.dotc.sbt`
50+
51+
> The sbt compiler phase is based on
52+
> https://github.com/adriaanm/scala/tree/sbt-api-consolidate/src/compiler/scala/tools/sbt
53+
> which attempts to integrate the sbt phases into scalac and is itself based on
54+
> the [compiler bridge in sbt 0.13](https://github.com/sbt/sbt/tree/0.13/compile/interface/src/main/scala/xsbt),
55+
> but has been heavily adapted and refactored.
56+
> Original authors were Mark Harrah, Grzegorz Kossakowski, Martin Duhemm, Adriaan Moors and others.

bin/dotc

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ SCALA_BINARY_VERSION=2.11
2323
SCALA_COMPILER_VERSION=$(getLastStringOnLineWith "scala-compiler")
2424
DOTTY_VERSION=$(getLastStringOnLineWith "version in")
2525
JLINE_VERSION=$(getLastStringOnLineWith "jline")
26+
SBT_VERSION=$(grep "sbt.version=" "$DOTTY_ROOT/project/build.properties" | sed 's/sbt.version=//')
2627
bootcp=true
2728
bootstrapped=false
2829
default_java_opts="-Xmx768m -Xms768m"
@@ -100,13 +101,19 @@ then
100101
JLINE_JAR=$HOME/.ivy2/cache/jline/jline/jars/jline-$JLINE_VERSION.jar
101102
fi
102103

103-
if [ ! -f "$SCALA_LIBRARY_JAR" -o ! -f "$SCALA_REFLECT_JAR" -o ! -f "$SCALA_COMPILER_JAR" -o ! -f "$JLINE_JAR" ]
104+
if [ "$SBT_INTERFACE_JAR" == "" ]
105+
then
106+
SBT_INTERFACE_JAR=$HOME/.ivy2/cache/org.scala-sbt/interface/jars/interface-$SBT_VERSION.jar
107+
fi
108+
109+
if [ ! -f "$SCALA_LIBRARY_JAR" -o ! -f "$SCALA_REFLECT_JAR" -o ! -f "$SCALA_COMPILER_JAR" -o ! -f "$JLINE_JAR" -o ! -f "$SBT_INTERFACE_JAR" ]
104110
then
105111
echo To use this script please set
106112
echo SCALA_LIBRARY_JAR to point to scala-library-$SCALA_VERSION.jar "(currently $SCALA_LIBRARY_JAR)"
107113
echo SCALA_REFLECT_JAR to point to scala-reflect-$SCALA_VERSION.jar "(currently $SCALA_REFLECT_JAR)"
108114
echo SCALA_COMPILER_JAR to point to scala-compiler-$SCALA_VERSION.jar with bcode patches "(currently $SCALA_COMPILER_JAR)"
109115
echo JLINE_JAR to point to jline-$JLINE_VERSION.jar "(currently $JLINE_JAR)"
116+
echo SBT_INTERFACE_JAR to point to interface-$SBT_VERSION.jar "(currently $SBT_INTERFACE_JAR)"
110117
fi
111118

112119
ifdebug () {
@@ -196,9 +203,9 @@ trap onExit INT
196203
classpathArgs () {
197204
if [[ "true" == $bootstrapped ]]; then
198205
checkjar $DOTTY_JAR "test:runMain dotc.build" src
199-
toolchain="$DOTTY_JAR:$SCALA_LIBRARY_JAR:$SCALA_REFLECT_JAR:$SCALA_COMPILER_JAR:$JLINE_JAR"
206+
toolchain="$DOTTY_JAR:$SCALA_LIBRARY_JAR:$SCALA_REFLECT_JAR:$SCALA_COMPILER_JAR:$JLINE_JAR:$SBT_INTERFACE_JAR"
200207
else
201-
toolchain="$SCALA_LIBRARY_JAR:$SCALA_REFLECT_JAR:$SCALA_COMPILER_JAR:$JLINE_JAR"
208+
toolchain="$SCALA_LIBRARY_JAR:$SCALA_REFLECT_JAR:$SCALA_COMPILER_JAR:$JLINE_JAR:$SBT_INTERFACE_JAR"
202209
fi
203210
bcpJars="$INTERFACES_JAR:$MAIN_JAR"
204211
cpJars="$INTERFACES_JAR:$MAIN_JAR:$TEST_JAR"

project/Build.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ object DottyBuild extends Build {
8484
libraryDependencies ++= Seq("org.scala-lang.modules" %% "scala-xml" % "1.0.1",
8585
"org.scala-lang.modules" %% "scala-partest" % "1.0.11" % "test",
8686
"com.novocode" % "junit-interface" % "0.11" % "test",
87-
"jline" % "jline" % "2.12"),
87+
"jline" % "jline" % "2.12",
88+
"org.scala-sbt" % "interface" % sbtVersion.value),
8889

8990
// enable improved incremental compilation algorithm
9091
incOptions := incOptions.value.withNameHashing(true),

src/dotty/tools/backend/jvm/GenBCode.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,8 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
389389
val className = jclassName.replace('/', '.')
390390
if (ctx.compilerCallback != null)
391391
ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(outFile), className)
392+
if (ctx.sbtCallback != null)
393+
ctx.sbtCallback.generatedClass(sourceFile.jfile.orElse(null), outFile.file, className)
392394
}
393395
catch {
394396
case e: FileConflictException =>

src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class Compiler {
4242
def phases: List[List[Phase]] =
4343
List(
4444
List(new FrontEnd), // Compiler frontend: scanner, parser, namer, typer
45+
List(new sbt.SbtPhase), // Gather information for sbt incremental compilation
4546
List(new PostTyper), // Additional checks and cleanups after type checking
4647
List(new Pickler), // Generate TASTY info
4748
List(new FirstTransform, // Some transformations to put trees into a canonical form

src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ class ScalaSettings extends Settings.SettingGroup {
160160
val YtestPickler = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler")
161161
val YcheckReentrant = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.")
162162
val YkeepComments = BooleanSetting("-Ykeep-comments", "Keep comments when scanning source files.")
163+
val YforceSbtPhase = BooleanSetting("-Yforce-sbt-phase", "Run the sbt incremental compiler phase even if the compiler is ran outside of sbt, for debugging.")
164+
val YdumpSbtApi = BooleanSetting("-Ydump-sbt-api", "For every compiled foo.scala, output the sbt API representation in foo.api, implies -Yforce-sbt-phase.")
163165
def stop = YstopAfter
164166

165167
/** Area-specific debug output.

src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import printing._
2929
import config.{Settings, ScalaSettings, Platform, JavaPlatform, SJSPlatform}
3030
import language.implicitConversions
3131
import DenotTransformers.DenotTransformer
32+
import xsbti.AnalysisCallback
3233

3334
object Contexts {
3435

@@ -84,6 +85,12 @@ object Contexts {
8485
_compilerCallback = callback
8586
def compilerCallback: CompilerCallback = _compilerCallback
8687

88+
/** The sbt callback implementation if we are run from sbt, null otherwise */
89+
private[this] var _sbtCallback: AnalysisCallback = _
90+
protected def sbtCallback_=(callback: AnalysisCallback) =
91+
_sbtCallback = callback
92+
def sbtCallback: AnalysisCallback = _sbtCallback
93+
8794
/** The current context */
8895
private[this] var _period: Period = _
8996
protected def period_=(period: Period) = {
@@ -426,6 +433,7 @@ object Contexts {
426433
def setPeriod(period: Period): this.type = { this.period = period; this }
427434
def setMode(mode: Mode): this.type = { this.mode = mode; this }
428435
def setCompilerCallback(callback: CompilerCallback): this.type = { this.compilerCallback = callback; this }
436+
def setSbtCallback(callback: AnalysisCallback): this.type = { this.sbtCallback = callback; this }
429437
def setTyperState(typerState: TyperState): this.type = { this.typerState = typerState; this }
430438
def setReporter(reporter: Reporter): this.type = setTyperState(typerState.withReporter(reporter))
431439
def setNewTyperState: this.type = setTyperState(typerState.fresh(isCommittable = true))

src/dotty/tools/dotc/core/Types.scala

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,10 +2478,14 @@ object Types {
24782478

24792479
abstract class ParamType extends BoundType {
24802480
def paramNum: Int
2481+
def paramName: Name
24812482
}
24822483

24832484
abstract case class MethodParam(binder: MethodType, paramNum: Int) extends ParamType with SingletonType {
24842485
type BT = MethodType
2486+
2487+
def paramName = binder.paramNames(paramNum)
2488+
24852489
override def underlying(implicit ctx: Context): Type = binder.paramTypes(paramNum)
24862490
def copyBoundType(bt: BT) = new MethodParamImpl(bt, paramNum)
24872491

@@ -2494,7 +2498,7 @@ object Types {
24942498
false
24952499
}
24962500

2497-
override def toString = s"MethodParam(${binder.paramNames(paramNum)})"
2501+
override def toString = s"MethodParam($paramName)"
24982502
}
24992503

25002504
class MethodParamImpl(binder: MethodType, paramNum: Int) extends MethodParam(binder, paramNum)
@@ -2524,9 +2528,11 @@ object Types {
25242528
case _ => false
25252529
}
25262530

2531+
def paramName = binder.paramNames(paramNum)
2532+
25272533
override def underlying(implicit ctx: Context): Type = binder.paramBounds(paramNum)
25282534
// no customized hashCode/equals needed because cycle is broken in PolyType
2529-
override def toString = s"PolyParam(${binder.paramNames(paramNum)})"
2535+
override def toString = s"PolyParam($paramName)"
25302536

25312537
override def computeHash = doHash(paramNum, binder.identityHash)
25322538

@@ -2778,9 +2784,9 @@ object Types {
27782784
if ((prefix eq cls.owner.thisType) || !cls.owner.isClass || ctx.erasedTypes) tp
27792785
else tp.substThis(cls.owner.asClass, prefix)
27802786

2781-
private var typeRefCache: Type = null
2787+
private var typeRefCache: TypeRef = null
27822788

2783-
def typeRef(implicit ctx: Context): Type = {
2789+
def typeRef(implicit ctx: Context): TypeRef = {
27842790
def clsDenot = if (prefix eq cls.owner.thisType) cls.denot else cls.denot.copySymDenotation(info = this)
27852791
if (typeRefCache == null)
27862792
typeRefCache =
@@ -2789,7 +2795,7 @@ object Types {
27892795
typeRefCache
27902796
}
27912797

2792-
def symbolicTypeRef(implicit ctx: Context): Type = TypeRef(prefix, cls)
2798+
def symbolicTypeRef(implicit ctx: Context): TypeRef = TypeRef(prefix, cls)
27932799

27942800
// cached because baseType needs parents
27952801
private var parentsCache: List[TypeRef] = null

0 commit comments

Comments
 (0)