Skip to content

Commit 3af0ebb

Browse files
authored
Merge pull request #2134 from dotty-staging/fix/incremental-compilation-restart
Fix incremental compilation not working after restarting sbt
2 parents e54b7e3 + 16ce4f7 commit 3af0ebb

File tree

1 file changed

+64
-4
lines changed

1 file changed

+64
-4
lines changed

compiler/src/dotty/tools/dotc/sbt/ThunkHolder.scala

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package sbt
34

45
import scala.annotation.tailrec
@@ -24,7 +25,7 @@ private[sbt] trait ThunkHolder {
2425
* It will be forced by the next call to `forceThunks()`
2526
*/
2627
def lzy[T <: AnyRef](t: => T): api.Lazy[T] = {
27-
val l = SafeLazy(() => t)
28+
val l = SafeLazyWrapper(() => t)
2829
thunks += l
2930
l
3031
}
@@ -37,10 +38,69 @@ private[sbt] trait ThunkHolder {
3738
* see https://github.com/sbt/zinc/issues/114
3839
*/
3940
def strict2lzy[T <: AnyRef](t: T): api.Lazy[T] =
40-
SafeLazy.strict(t)
41+
SafeLazyWrapper.strict(t)
4142
}
4243

43-
// TODO: Use xsbti.SafeLazy once https://github.com/sbt/zinc/issues/113 is fixed
44+
/** Wrapper around SafeLazy implementations.
45+
*
46+
* `xsbti.SafeLazy` is part of sbt but it is not part of the `interface` jar
47+
* that dotty depends on, therefore we can only access it by reflection,
48+
* and this will only succeed when dotty is run by sbt (otherwise
49+
* `xsbti.SafeLazy` won't be on the classpath at all).
50+
*
51+
* For testing purposes, we still want to be able to run the sbt phases outside
52+
* of sbt, using `-Yforce-sbt-phases` and `-Ydump-sbt-inc`, therefore we
53+
* provide a copy of SafeLazy in `dotty.tools.dotc.sbt.SafeLazy` that we use
54+
* when `xsbti.SafeLazy` is unavailable.
55+
*
56+
* This raises a question: why bother with `xsbti.SafeLazy` if we have our own
57+
* version anyway? Because sbt uses Java serialization to persist the output of
58+
* the incremental compilation analysis when sbt is stopped and restarted. If
59+
* we used `dotty.tools.dotc.sbt.SafeLazy` with sbt, deserialization would fail
60+
* and every restart of sbt would require a full recompilation.
61+
*
62+
* Note: this won't be needed once we switch to zinc 1.0 where `SafeLazy` becomes
63+
* part of the `interface` jar, see https://github.com/sbt/zinc/issues/113
64+
*/
65+
private object SafeLazyWrapper {
66+
67+
@sharable private[this] val safeLazy =
68+
try {
69+
Class.forName("xsbti.SafeLazy")
70+
} catch {
71+
case e: ClassNotFoundException =>
72+
null
73+
}
74+
75+
@sharable private[this] val safeLazyApply =
76+
if (safeLazy != null)
77+
safeLazy.getMethod("apply", classOf[xsbti.F0[_]])
78+
else
79+
null
80+
@sharable private[this] val safeLazyStrict =
81+
if (safeLazy != null)
82+
safeLazy.getMethod("strict", classOf[Object])
83+
else
84+
null
85+
86+
def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] =
87+
if (safeLazyApply != null)
88+
safeLazyApply
89+
.invoke(null, new xsbti.F0[T] { def apply() = eval() })
90+
.asInstanceOf[xsbti.api.Lazy[T]]
91+
else
92+
SafeLazy(eval)
93+
94+
def strict[T <: AnyRef](value: T): xsbti.api.Lazy[T] =
95+
if (safeLazyStrict != null)
96+
safeLazyStrict
97+
.invoke(null, value)
98+
.asInstanceOf[xsbti.api.Lazy[T]]
99+
else
100+
SafeLazy.strict(value)
101+
}
102+
103+
// Adapted from https://github.com/sbt/sbt/blob/0.13/compile/api/src/main/scala/xsbti/SafeLazy.scala
44104
private object SafeLazy {
45105
def apply[T <: AnyRef](eval: () => T): xsbti.api.Lazy[T] =
46106
new Impl(eval)

0 commit comments

Comments
 (0)