@@ -14,126 +14,59 @@ import scala.tools.nsc.transform.TypingTransformers
1414/** @author Stephen Samuel */
1515class ScoveragePlugin (val global : Global ) extends Plugin {
1616
17- override val name : String = " scoverage"
18- override val description : String = " scoverage code coverage compiler plugin"
19- private val (extraAfterPhase, extraBeforePhase) = processPhaseOptions(
20- pluginOptions
21- )
17+ override val name : String = ScoveragePlugin .name
18+ override val description : String = ScoveragePlugin .description
19+
20+ // TODO I'm not 100% sure why, but historically these have been parsed out
21+ // first. One thing to play around with in the future would be to not do this
22+ // here and rather do it later when we utilize setOpts and instead just
23+ // initialize then in the instrumentationCompoent. This will save us
24+ // iterating over these options twice.
25+ private val (extraAfterPhase, extraBeforePhase) =
26+ ScoverageOptions .processPhaseOptions(
27+ pluginOptions
28+ )
29+
2230 val instrumentationComponent = new ScoverageInstrumentationComponent (
2331 global,
2432 extraAfterPhase,
2533 extraBeforePhase
2634 )
35+
2736 override val components : List [PluginComponent ] = List (
2837 instrumentationComponent
2938 )
3039
31- private def parseExclusionEntry (
32- entryName : String ,
33- inOption : String
34- ): Seq [String ] =
35- inOption
36- .substring(entryName.length)
37- .split(" ;" )
38- .map(_.trim)
39- .toIndexedSeq
40- .filterNot(_.isEmpty)
41-
4240 override def init (opts : List [String ], error : String => Unit ): Boolean = {
43- val options = new ScoverageOptions
44-
45- for (opt <- opts) {
46- if (opt.startsWith(" excludedPackages:" )) {
47- options.excludedPackages = parseExclusionEntry(" excludedPackages:" , opt)
48- } else if (opt.startsWith(" excludedFiles:" )) {
49- options.excludedFiles = parseExclusionEntry(" excludedFiles:" , opt)
50- } else if (opt.startsWith(" excludedSymbols:" )) {
51- options.excludedSymbols = parseExclusionEntry(" excludedSymbols:" , opt)
52- } else if (opt.startsWith(" dataDir:" )) {
53- options.dataDir = opt.substring(" dataDir:" .length)
54- } else if (opt.startsWith(" sourceRoot:" )) {
55- options.sourceRoot = opt.substring(" sourceRoot:" .length())
56- } else if (
57- opt
58- .startsWith(" extraAfterPhase:" ) || opt.startsWith(" extraBeforePhase:" )
59- ) {
60- // skip here, these flags are processed elsewhere
61- } else if (opt == " reportTestName" ) {
62- options.reportTestName = true
63- } else {
64- error(" Unknown option: " + opt)
65- }
66- }
67- if (! opts.exists(_.startsWith(" dataDir:" )))
41+
42+ val options =
43+ ScoverageOptions .parse(opts, error, ScoverageOptions .default())
44+
45+ if (options.dataDir.isEmpty())
6846 throw new RuntimeException (
6947 " Cannot invoke plugin without specifying <dataDir>"
7048 )
71- if (! opts.exists(_.startsWith(" sourceRoot:" )))
49+
50+ if (options.sourceRoot.isEmpty())
7251 throw new RuntimeException (
7352 " Cannot invoke plugin without specifying <sourceRoot>"
7453 )
54+
7555 instrumentationComponent.setOptions(options)
7656 true
7757 }
7858
79- override val optionsHelp : Option [String ] = Some (
80- Seq (
81- " -P:scoverage:dataDir:<pathtodatadir> where the coverage files should be written\n " ,
82- " -P:scoverage:sourceRoot:<pathtosourceRoot> the root dir of your sources, used for path relativization\n " ,
83- " -P:scoverage:excludedPackages:<regex>;<regex> semicolon separated list of regexs for packages to exclude" ,
84- " -P:scoverage:excludedFiles:<regex>;<regex> semicolon separated list of regexs for paths to exclude" ,
85- " -P:scoverage:excludedSymbols:<regex>;<regex> semicolon separated list of regexs for symbols to exclude" ,
86- " -P:scoverage:extraAfterPhase:<phaseName> phase after which scoverage phase runs (must be after typer phase)" ,
87- " -P:scoverage:extraBeforePhase:<phaseName> phase before which scoverage phase runs (must be before patmat phase)" ,
88- " Any classes whose fully qualified name matches the regex will" ,
89- " be excluded from coverage."
90- ).mkString(" \n " )
91- )
59+ override val optionsHelp : Option [String ] = ScoverageOptions .help
9260
93- // copied from scala 2.11
9461 private def pluginOptions : List [String ] = {
9562 // Process plugin options of form plugin:option
9663 def namec = name + " :"
97- global.settings.pluginOptions.value filter (_ startsWith namec) map (_ stripPrefix namec)
98- }
99-
100- private def processPhaseOptions (
101- opts : List [String ]
102- ): (Option [String ], Option [String ]) = {
103- var afterPhase : Option [String ] = None
104- var beforePhase : Option [String ] = None
105- for (opt <- opts) {
106- if (opt.startsWith(" extraAfterPhase:" )) {
107- afterPhase = Some (opt.substring(" extraAfterPhase:" .length))
108- }
109- if (opt.startsWith(" extraBeforePhase:" )) {
110- beforePhase = Some (opt.substring(" extraBeforePhase:" .length))
111- }
112- }
113- (afterPhase, beforePhase)
64+ global.settings.pluginOptions.value
65+ .filter(_.startsWith(namec))
66+ .map(_.stripPrefix(namec))
11467 }
11568}
11669
117- // TODO refactor this into a case class. We'll also refactor how we parse the
118- // options to get rid of all these vars
119- class ScoverageOptions {
120- var excludedPackages : Seq [String ] = Nil
121- var excludedFiles : Seq [String ] = Nil
122- var excludedSymbols : Seq [String ] = Seq (
123- " scala.reflect.api.Exprs.Expr" ,
124- " scala.reflect.api.Trees.Tree" ,
125- " scala.reflect.macros.Universe.Tree"
126- )
127- var dataDir : String = IOUtils .getTempPath
128- var reportTestName : Boolean = false
129- // TODO again, we'll refactor this later so this won't have a default here.
130- // However for tests we'll have to create this. However, make sure you create
131- // either both in temp or neither in temp, since on windows your temp dir
132- // will be in another drive, so the relativize functinality won't work if
133- // correctly.
134- var sourceRoot : String = IOUtils .getTempPath
135- }
136-
13770class ScoverageInstrumentationComponent (
13871 val global : Global ,
13972 extraAfterPhase : Option [String ],
@@ -147,7 +80,7 @@ class ScoverageInstrumentationComponent(
14780 val statementIds = new AtomicInteger (0 )
14881 val coverage = new Coverage
14982
150- override val phaseName : String = " scoverage-instrumentation "
83+ override val phaseName : String = ScoveragePlugin .phaseName
15184 override val runsAfter : List [String ] =
15285 List (" typer" ) ::: extraAfterPhase.toList
15386 override val runsBefore : List [String ] =
@@ -158,7 +91,7 @@ class ScoverageInstrumentationComponent(
15891 * You must call "setOptions" before running any commands that rely on
15992 * the options.
16093 */
161- private var options : ScoverageOptions = new ScoverageOptions ()
94+ private var options : ScoverageOptions = ScoverageOptions .default ()
16295 private var coverageFilter : CoverageFilter = AllCoverageFilter
16396
16497 private val isScalaJsEnabled : Boolean = {
@@ -194,7 +127,6 @@ class ScoverageInstrumentationComponent(
194127 s " Instrumentation completed [ ${coverage.statements.size} statements] "
195128 )
196129
197- // TODO do we need to verify this sourceRoot exists? How does semanticdb do this?
198130 Serializer .serialize(
199131 coverage,
200132 Serializer .coverageFile(options.dataDir),
@@ -920,3 +852,9 @@ class ScoverageInstrumentationComponent(
920852 }
921853 }
922854}
855+
856+ object ScoveragePlugin {
857+ val name : String = " scoverage"
858+ val description : String = " scoverage code coverage compiler plugin"
859+ val phaseName : String = " scoverage-instrumentation"
860+ }
0 commit comments