Skip to content

Commit f196558

Browse files
committed
change encoding MegaPhase name in progress tracking
also add sbt-bridge test for CompileProgress
1 parent 619ccf3 commit f196558

File tree

7 files changed

+129
-18
lines changed

7 files changed

+129
-18
lines changed

Diff for: compiler/src/dotty/tools/dotc/Run.scala

+8-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import scala.io.Codec
3636

3737
import Run.Progress
3838
import scala.compiletime.uninitialized
39+
import dotty.tools.dotc.transform.MegaPhase
3940

4041
/** A compiler run. Exports various methods to compile source files */
4142
class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with ConstraintRunInfo {
@@ -463,7 +464,11 @@ object Run {
463464
class SubPhases(val phase: Phase):
464465
require(phase.exists)
465466

466-
val all = IArray.from(phase.subPhases.map(sub => s"${phase.phaseName} ($sub)"))
467+
private def baseName: String = phase match
468+
case phase: MegaPhase => phase.shortPhaseName
469+
case phase => phase.phaseName
470+
471+
val all = IArray.from(phase.subPhases.map(sub => s"$baseName ($sub)"))
467472

468473
def next(using Context): Option[SubPhases] =
469474
val next0 = phase.megaPhase.next.megaPhase
@@ -472,7 +477,8 @@ object Run {
472477

473478
def subPhase(index: Int) =
474479
if index < all.size then all(index)
475-
else phase.phaseName
480+
else baseName
481+
476482

477483
private class Progress(cb: ProgressCallback, private val run: Run, val initialTraversals: Int):
478484
private[Run] var totalTraversals: Int = initialTraversals // track how many phases we expect to run

Diff for: compiler/src/dotty/tools/dotc/transform/MegaPhase.scala

+6
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
145145
if (miniPhases.length == 1) miniPhases(0).phaseName
146146
else miniPhases.map(_.phaseName).mkString("MegaPhase{", ", ", "}")
147147

148+
/** Used in progress reporting to avoid super long phase names, also the precision is not so important here */
149+
lazy val shortPhaseName: String =
150+
if (miniPhases.length == 1) miniPhases(0).phaseName
151+
else
152+
s"MegaPhase{${miniPhases.head.phaseName},...,${miniPhases.last.phaseName}}"
153+
148154
private var relaxedTypingCache: Boolean = _
149155
private var relaxedTypingKnown = false
150156

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package xsbt
2+
3+
import org.junit.{ Test, Ignore }
4+
import org.junit.Assert._
5+
6+
/**Only does some rudimentary checks to assert compat with sbt.
7+
* More thorough tests are found in compiler/test/dotty/tools/dotc/sbt/ProgressCallbackTest.scala
8+
*/
9+
class CompileProgressSpecification {
10+
11+
@Test
12+
def multipleFilesVisitSamePhases = {
13+
val srcA = """class A"""
14+
val srcB = """class B"""
15+
val compilerForTesting = new ScalaCompilerForUnitTesting
16+
val Seq(phasesA, phasesB) = compilerForTesting.extractEnteredPhases(srcA, srcB)
17+
assertTrue("expected some phases, was empty", phasesA.nonEmpty)
18+
assertEquals(phasesA, phasesB)
19+
}
20+
21+
@Test
22+
def multipleFiles = {
23+
val srcA = """class A"""
24+
val srcB = """class B"""
25+
val compilerForTesting = new ScalaCompilerForUnitTesting
26+
val allPhases = compilerForTesting.extractProgressPhases(srcA, srcB)
27+
assertTrue("expected some phases, was empty", allPhases.nonEmpty)
28+
val someExpectedPhases = // just check some "fundamental" phases, don't put all phases to avoid brittleness
29+
Set(
30+
"parser",
31+
"typer (indexing)", "typer (typechecking)", "typer (checking java)",
32+
"sbt-deps",
33+
"extractSemanticDB",
34+
"posttyper",
35+
"sbt-api",
36+
"SetRootTree",
37+
"pickler",
38+
"inlining",
39+
"postInlining",
40+
"staging",
41+
"splicing",
42+
"pickleQuotes",
43+
"MegaPhase{pruneErasedDefs,...,arrayConstructors}",
44+
"erasure",
45+
"constructors",
46+
"genSJSIR",
47+
"genBCode"
48+
)
49+
val missingExpectedPhases = someExpectedPhases -- allPhases.toSet
50+
val msgIfMissing =
51+
s"missing expected phases: $missingExpectedPhases. " +
52+
s"Either the compiler phases changed, or the encoding of Run.SubPhases.subPhase"
53+
assertTrue(msgIfMissing, missingExpectedPhases.isEmpty)
54+
}
55+
56+
}

Diff for: sbt-bridge/test/xsbt/ExtractUsedNamesSpecification.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package xsbt
22

33
import xsbti.UseScope
4+
import ScalaCompilerForUnitTesting.Callbacks
45

56
import org.junit.{ Test, Ignore }
67
import org.junit.Assert._
@@ -226,7 +227,7 @@ class ExtractUsedNamesSpecification {
226227

227228
def findPatMatUsages(in: String): Set[String] = {
228229
val compilerForTesting = new ScalaCompilerForUnitTesting
229-
val (_, callback) =
230+
val (_, Callbacks(callback, _)) =
230231
compilerForTesting.compileSrcs(List(List(sealedClass, in)))
231232
val clientNames = callback.usedNamesAndScopes.view.filterKeys(!_.startsWith("base."))
232233

Diff for: sbt-bridge/test/xsbt/ScalaCompilerForUnitTesting.scala

+27-10
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,34 @@ import dotty.tools.io.PlainFile.toPlainFile
1313
import dotty.tools.xsbt.CompilerBridge
1414

1515
import TestCallback.ExtractedClassDependencies
16+
import ScalaCompilerForUnitTesting.Callbacks
17+
18+
object ScalaCompilerForUnitTesting:
19+
case class Callbacks(analysis: TestCallback, progress: TestCompileProgress)
1620

1721
/**
1822
* Provides common functionality needed for unit tests that require compiling
1923
* source code using Scala compiler.
2024
*/
2125
class ScalaCompilerForUnitTesting {
2226

27+
def extractEnteredPhases(srcs: String*): Seq[List[String]] = {
28+
val (tempSrcFiles, Callbacks(_, testProgress)) = compileSrcs(srcs: _*)
29+
val run = testProgress.runs.head
30+
tempSrcFiles.map(src => run.unitPhases(src.id))
31+
}
32+
33+
def extractProgressPhases(srcs: String*): List[String] = {
34+
val (_, Callbacks(_, testProgress)) = compileSrcs(srcs: _*)
35+
testProgress.runs.head.phases
36+
}
37+
2338
/**
2439
* Compiles given source code using Scala compiler and returns API representation
2540
* extracted by ExtractAPI class.
2641
*/
2742
def extractApiFromSrc(src: String): Seq[ClassLike] = {
28-
val (Seq(tempSrcFile), analysisCallback) = compileSrcs(src)
43+
val (Seq(tempSrcFile), Callbacks(analysisCallback, _)) = compileSrcs(src)
2944
analysisCallback.apis(tempSrcFile)
3045
}
3146

@@ -34,7 +49,7 @@ class ScalaCompilerForUnitTesting {
3449
* extracted by ExtractAPI class.
3550
*/
3651
def extractApisFromSrcs(srcs: List[String]*): Seq[Seq[ClassLike]] = {
37-
val (tempSrcFiles, analysisCallback) = compileSrcs(srcs.toList)
52+
val (tempSrcFiles, Callbacks(analysisCallback, _)) = compileSrcs(srcs.toList)
3853
tempSrcFiles.map(analysisCallback.apis)
3954
}
4055

@@ -52,7 +67,7 @@ class ScalaCompilerForUnitTesting {
5267
assertDefaultScope: Boolean = true
5368
): Map[String, Set[String]] = {
5469
// we drop temp src file corresponding to the definition src file
55-
val (Seq(_, tempSrcFile), analysisCallback) = compileSrcs(definitionSrc, actualSrc)
70+
val (Seq(_, tempSrcFile), Callbacks(analysisCallback, _)) = compileSrcs(definitionSrc, actualSrc)
5671

5772
if (assertDefaultScope) for {
5873
(className, used) <- analysisCallback.usedNamesAndScopes
@@ -70,7 +85,7 @@ class ScalaCompilerForUnitTesting {
7085
* Only the names used in the last src file are returned.
7186
*/
7287
def extractUsedNamesFromSrc(sources: String*): Map[String, Set[String]] = {
73-
val (srcFiles, analysisCallback) = compileSrcs(sources: _*)
88+
val (srcFiles, Callbacks(analysisCallback, _)) = compileSrcs(sources: _*)
7489
srcFiles
7590
.map { srcFile =>
7691
val classesInSrc = analysisCallback.classNames(srcFile).map(_._1)
@@ -92,7 +107,7 @@ class ScalaCompilerForUnitTesting {
92107
* file system-independent way of testing dependencies between source code "files".
93108
*/
94109
def extractDependenciesFromSrcs(srcs: List[List[String]]): ExtractedClassDependencies = {
95-
val (_, testCallback) = compileSrcs(srcs)
110+
val (_, Callbacks(testCallback, _)) = compileSrcs(srcs)
96111

97112
val memberRefDeps = testCallback.classDependencies collect {
98113
case (target, src, DependencyByMemberRef) => (src, target)
@@ -121,7 +136,7 @@ class ScalaCompilerForUnitTesting {
121136
* The sequence of temporary files corresponding to passed snippets and analysis
122137
* callback is returned as a result.
123138
*/
124-
def compileSrcs(groupedSrcs: List[List[String]]): (Seq[VirtualFile], TestCallback) = {
139+
def compileSrcs(groupedSrcs: List[List[String]]): (Seq[VirtualFile], Callbacks) = {
125140
val temp = IO.createTemporaryDirectory
126141
val analysisCallback = new TestCallback
127142
val testProgress = new TestCompileProgress
@@ -130,8 +145,8 @@ class ScalaCompilerForUnitTesting {
130145

131146
val bridge = new CompilerBridge
132147

133-
val files = for ((compilationUnit, unitId) <- groupedSrcs.zipWithIndex) yield {
134-
val srcFiles = compilationUnit.toSeq.zipWithIndex.map {
148+
val files = for ((compilationUnits, unitId) <- groupedSrcs.zipWithIndex) yield {
149+
val srcFiles = compilationUnits.toSeq.zipWithIndex.map {
135150
(src, i) =>
136151
val fileName = s"Test-$unitId-$i.scala"
137152
prepareSrcFile(temp, fileName, src)
@@ -153,12 +168,14 @@ class ScalaCompilerForUnitTesting {
153168
new TestLogger
154169
)
155170

171+
testProgress.completeRun()
172+
156173
srcFiles
157174
}
158-
(files.flatten.toSeq, analysisCallback)
175+
(files.flatten.toSeq, Callbacks(analysisCallback, testProgress))
159176
}
160177

161-
def compileSrcs(srcs: String*): (Seq[VirtualFile], TestCallback) = {
178+
def compileSrcs(srcs: String*): (Seq[VirtualFile], Callbacks) = {
162179
compileSrcs(List(srcs.toList))
163180
}
164181

Diff for: sbt-bridge/test/xsbt/TestCompileProgress.scala

-5
This file was deleted.

Diff for: sbt-bridge/test/xsbti/TestCompileProgress.scala

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package xsbti
2+
3+
import xsbti.compile.CompileProgress
4+
5+
import scala.collection.mutable
6+
7+
class TestCompileProgress extends CompileProgress:
8+
class Run:
9+
private[TestCompileProgress] val _phases: mutable.Set[String] = mutable.LinkedHashSet.empty
10+
private[TestCompileProgress] val _unitPhases: mutable.Map[String, mutable.Set[String]] = mutable.LinkedHashMap.empty
11+
12+
def phases: List[String] = _phases.toList
13+
def unitPhases: collection.MapView[String, List[String]] = _unitPhases.view.mapValues(_.toList)
14+
15+
private val _runs: mutable.ListBuffer[Run] = mutable.ListBuffer.empty
16+
private var _currentRun: Run = new Run
17+
18+
def runs: List[Run] = _runs.toList
19+
20+
def completeRun(): Unit =
21+
_runs += _currentRun
22+
_currentRun = new Run
23+
24+
override def startUnit(phase: String, unitPath: String): Unit =
25+
_currentRun._unitPhases.getOrElseUpdate(unitPath, mutable.LinkedHashSet.empty) += phase
26+
27+
override def advance(current: Int, total: Int, prevPhase: String, nextPhase: String): Boolean =
28+
_currentRun._phases += prevPhase
29+
_currentRun._phases += nextPhase
30+
true

0 commit comments

Comments
 (0)