@@ -14,126 +14,59 @@ import scala.tools.nsc.transform.TypingTransformers
14
14
/** @author Stephen Samuel */
15
15
class ScoveragePlugin (val global : Global ) extends Plugin {
16
16
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
+
22
30
val instrumentationComponent = new ScoverageInstrumentationComponent (
23
31
global,
24
32
extraAfterPhase,
25
33
extraBeforePhase
26
34
)
35
+
27
36
override val components : List [PluginComponent ] = List (
28
37
instrumentationComponent
29
38
)
30
39
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
-
42
40
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())
68
46
throw new RuntimeException (
69
47
" Cannot invoke plugin without specifying <dataDir>"
70
48
)
71
- if (! opts.exists(_.startsWith(" sourceRoot:" )))
49
+
50
+ if (options.sourceRoot.isEmpty())
72
51
throw new RuntimeException (
73
52
" Cannot invoke plugin without specifying <sourceRoot>"
74
53
)
54
+
75
55
instrumentationComponent.setOptions(options)
76
56
true
77
57
}
78
58
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
92
60
93
- // copied from scala 2.11
94
61
private def pluginOptions : List [String ] = {
95
62
// Process plugin options of form plugin:option
96
63
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))
114
67
}
115
68
}
116
69
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
-
137
70
class ScoverageInstrumentationComponent (
138
71
val global : Global ,
139
72
extraAfterPhase : Option [String ],
@@ -147,7 +80,7 @@ class ScoverageInstrumentationComponent(
147
80
val statementIds = new AtomicInteger (0 )
148
81
val coverage = new Coverage
149
82
150
- override val phaseName : String = " scoverage-instrumentation "
83
+ override val phaseName : String = ScoveragePlugin .phaseName
151
84
override val runsAfter : List [String ] =
152
85
List (" typer" ) ::: extraAfterPhase.toList
153
86
override val runsBefore : List [String ] =
@@ -158,7 +91,7 @@ class ScoverageInstrumentationComponent(
158
91
* You must call "setOptions" before running any commands that rely on
159
92
* the options.
160
93
*/
161
- private var options : ScoverageOptions = new ScoverageOptions ()
94
+ private var options : ScoverageOptions = ScoverageOptions .default ()
162
95
private var coverageFilter : CoverageFilter = AllCoverageFilter
163
96
164
97
private val isScalaJsEnabled : Boolean = {
@@ -194,7 +127,6 @@ class ScoverageInstrumentationComponent(
194
127
s " Instrumentation completed [ ${coverage.statements.size} statements] "
195
128
)
196
129
197
- // TODO do we need to verify this sourceRoot exists? How does semanticdb do this?
198
130
Serializer .serialize(
199
131
coverage,
200
132
Serializer .coverageFile(options.dataDir),
@@ -920,3 +852,9 @@ class ScoverageInstrumentationComponent(
920
852
}
921
853
}
922
854
}
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