Skip to content

Commit 626e99e

Browse files
author
Sameer Arora
committed
adding a targetId to help grab correct instrument file at runtime from cp
1 parent edf30b3 commit 626e99e

File tree

5 files changed

+134
-168
lines changed

5 files changed

+134
-168
lines changed

build.sbt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ lazy val runtime = CrossProject("scalac-scoverage-runtime", file("scalac-scovera
7171
.settings(appSettings: _*)
7272
.jvmSettings(
7373
fork in Test := true,
74-
envVars in Test := Map("SCOVERAGE_MEASUREMENT_PATH" -> "scoverageMeasurementFiles"),
7574
libraryDependencies ++= Seq(
7675
"org.scalatest" %% "scalatest" % ScalatestVersion % "test"
7776
)
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package scoverage
22

33
object Constants {
4-
// the file that contains the statement mappings
5-
val CoverageFileName = "scoverage.coverage"
64
// the final scoverage report
75
val XMLReportFilename = "scoverage.xml"
86
val XMLReportFilenameWithDebug = "scoverage-debug.xml"
97
// directory that contains all the measurement data but not reports
108
val DataDir = "scoverage-data"
9+
/*********************************************************************
10+
* If updating any of the files below, an update is also required to *
11+
* the corresponding file name in Invoker.scala *
12+
*********************************************************************/
13+
// the file that contains the statement mappings
14+
val CoverageFileName = "scoverage.coverage"
1115
// the prefix the measurement files have
1216
val MeasurementsPrefix = "scoverage.measurements."
17+
//subdir to suffix to classpath when [writeToClasspath] option is in use"
18+
val ClasspathSubdir = "META-INF/scoverage"
1319
}

scalac-scoverage-plugin/src/main/scala/scoverage/plugin.scala

Lines changed: 63 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ class ScoveragePlugin(val global: Global) extends Plugin {
3434
options.dataDir = opt.substring("dataDir:".length)
3535
} else if (opt.startsWith("extraAfterPhase:") || opt.startsWith("extraBeforePhase:")) {
3636
// skip here, these flags are processed elsewhere
37-
} else if (opt.startsWith("classpathSubdir:")){
38-
options.classpathSubdir = opt.substring("classpathSubdir:".length)
39-
}else if (opt.startsWith("writeToClasspath:")) {
37+
} else if (opt.startsWith("writeToClasspath:")) {
4038
try {
4139
options.writeToClasspath = opt.substring("writeToClasspath:".length).toBoolean
4240
} catch {
@@ -56,10 +54,10 @@ class ScoveragePlugin(val global: Global) extends Plugin {
5654
}
5755

5856
override val optionsHelp: Option[String] = Some(Seq(
59-
"-P:scoverage:writeToClasspath:<boolean> if true, use the environment variable to store measurements" +
60-
"and store instruments to the output classpath where compiler emits classfiles." +
57+
"-P:scoverage:writeToClasspath:<boolean> if true, use the system property variable [scoverage_measurement_path]" +
58+
" to store measurements " +
59+
"and store instruments file to the output classpath where compiler emits classfiles. " +
6160
"Overrides the datadir with the output classpath.",
62-
"-P:scoverage:classpathSubdir:<pathtosubdir> subdir to attach to classpath. Default: META-INF/scoverage",
6361

6462
"-P:scoverage:dataDir:<pathtodatadir> where the coverage files should be written\n",
6563
"-P:scoverage:excludedPackages:<regex>;<regex> semicolon separated list of regexs for packages to exclude",
@@ -101,13 +99,12 @@ class ScoverageOptions {
10199

102100
// Adding additional option for https://github.com/scoverage/scalac-scoverage-plugin/issues/265
103101
// If the option is false, scoverage works the usual way.
104-
// If the option is true, the instruments are stored on to the classpath and
105-
// the measurements are written to the directory specified by the environment variable
106-
// `SCOVERAGE_MEASUREMENT_PATH`. This means setting [writeToClasspath] to true overrides the [dataDir]
102+
// If the option is true, the instrument files [scoverage.coverage] are stored on to the classpath and
103+
// the measurement files are written to the directory specified by the system property
104+
// `scoverage_measurement_path`. This means setting [writeToClasspath] to true overrides the [dataDir]
107105
// as the [dataDir] will now point to the output classpath of the compiler.
108106
// By default, the option is set to false.
109107
var writeToClasspath: Boolean = false
110-
var classpathSubdir: String = "META-INF/scoverage"
111108
}
112109

113110
class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Option[String], extraBeforePhase: Option[String])
@@ -133,6 +130,27 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt
133130
private var options: ScoverageOptions = new ScoverageOptions()
134131
private var coverageFilter: CoverageFilter = AllCoverageFilter
135132

133+
// This [targetId] is used below in case [writeToClasspath] option
134+
// is true. It is used for:
135+
// 1. Creating a unique subdir under the classpath to store the instrument files, i.e
136+
// path to instrument files will be `< classpath >/META-INF/scoverage/< targetId >/scoverage.coverage`
137+
// 2. Instrumenting the binaries with the [targetId], i.e binaries will be instrumented with
138+
// `Invoker.invokedWriteToClasspath(< statement id >, < targetId >)`
139+
// Consequently, at runtime, we can get the "correct" instrument file located at `.../< targetId >/scoverage.coverage`
140+
// from the classpath and store it with its appropriate measurements file.
141+
private var targetId: String = ""
142+
143+
// used for generating the targetId.
144+
def md5HashString(s: String): String = {
145+
import java.security.MessageDigest
146+
import java.math.BigInteger
147+
val md = MessageDigest.getInstance("MD5")
148+
val digest = md.digest(s.getBytes)
149+
val bigInt = new BigInteger(1,digest)
150+
val hashedString = bigInt.toString(16)
151+
hashedString
152+
}
153+
136154
def setOptions(options: ScoverageOptions): Unit = {
137155

138156
this.options = options
@@ -142,7 +160,11 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt
142160
// i.e to the directory where compiler is going to output the classfiles.
143161
if(options.writeToClasspath){
144162
settings.outputDirs.getSingleOutput match {
145-
case Some(dest) => options.dataDir = s"${dest.toString()}/${options.classpathSubdir}"
163+
case Some(dest) =>
164+
targetId = md5HashString(s"${dest.toString()}")
165+
// Setting [dataDir] to `< classpath >/META-INF/scoverage/< targetId >`.
166+
// This is where the instrument file [scoverage.coverage] for this target will now be stored.
167+
options.dataDir = s"${dest.toString()}/${Constants.ClasspathSubdir}/$targetId"
146168
case None => throw new RuntimeException("No output classpath specified.")
147169
}
148170
}
@@ -173,8 +195,8 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt
173195
reporter.echo(s"Will write measurement data to [${ options.dataDir }]")
174196
}
175197
else {
176-
reporter.echo(s"Will write measurements data to the directory specified by environment" +
177-
s" variable SCOVERAGE_MEASUREMENT_PATH at runtime.")
198+
reporter.echo(s"Will write measurements data to the directory specified by system" +
199+
s" variable scoverage_measurement_path at runtime.")
178200
}
179201
}
180202
}
@@ -199,49 +221,41 @@ class ScoverageInstrumentationComponent(val global: Global, extraAfterPhase: Opt
199221
def safeLine(tree: Tree): Int = if (tree.pos.isDefined) tree.pos.line else -1
200222
def safeSource(tree: Tree): Option[SourceFile] = if (tree.pos.isDefined) Some(tree.pos.source) else None
201223

202-
def invokeCall(id: Int): Tree = {
224+
var termName: String = ""
225+
var secondArg: String = ""
226+
if (options.writeToClasspath){
203227
/**
204-
* We still pass in [options.dataDir] as one of the instruments with
205-
* [invokedUseEnvironment] as it helps differentiating the source
206-
* files at runtime, i.e helps in creating a unique subdir for each source file.
228+
* We pass in [targetId] as one of the instruments with
229+
* [invokedUseEnvironment] as it helps in grabbing the "correct" instruments
230+
* file from the classpath at runtime. It also helps in
231+
* differentiating the targets at runtime, i.e helps in creating a unique subdir for each target.
207232
*/
208-
if (options.writeToClasspath){
209-
Apply(
233+
termName = "invokedWriteToClasspath"
234+
secondArg = targetId
235+
}
236+
else{
237+
termName = "invoked"
238+
secondArg = options.dataDir
239+
}
240+
241+
def invokeCall(id: Int): Tree = {
242+
Apply(
243+
Select(
210244
Select(
211-
Select(
212-
Ident("scoverage"),
213-
newTermName("Invoker")
214-
),
215-
newTermName("invokedWriteToClasspath")
245+
Ident("scoverage"),
246+
newTermName("Invoker")
216247
),
217-
List(
218-
Literal(
219-
Constant(id)
220-
),
221-
Literal(
222-
Constant(options.dataDir)
223-
)
224-
)
225-
)
226-
}else {
227-
Apply(
228-
Select(
229-
Select(
230-
Ident("scoverage"),
231-
newTermName("Invoker")
232-
),
233-
newTermName("invoked")
248+
newTermName(termName)
249+
),
250+
List(
251+
Literal(
252+
Constant(id)
234253
),
235-
List(
236-
Literal(
237-
Constant(id)
238-
),
239-
Literal(
240-
Constant(options.dataDir)
241-
)
254+
Literal(
255+
Constant(secondArg)
242256
)
243257
)
244-
}
258+
)
245259
}
246260

247261
override def transform(tree: Tree) = process(tree)
Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package scoverage
22

3-
import java.nio.file.{Files, Paths}
43
import org.scalatest.{BeforeAndAfter, FunSuite}
54
import scoverage.Platform.File
65

@@ -10,58 +9,47 @@ import scoverage.Platform.File
109
class InvokerUseEnvironmentTest extends FunSuite with BeforeAndAfter {
1110

1211

13-
val instrumentsDir = Array(
14-
new File("target/invoker-test.instrument0"),
15-
new File("target/invoker-test.instrument1")
12+
val targetIds = Array(
13+
new File("target_id_0"),
14+
new File("target_id_1")
1615
)
1716

1817
before {
19-
deleteInstrumentFiles()
20-
instrumentsDir.foreach(_.mkdirs())
21-
createNewInstruments()
18+
System.setProperty("scoverage_measurement_path","scoverage_java_prop")
2219
}
2320

24-
test("calling Invoker.invokedUseEnvironments puts the data on env var SCOVERAGE_MEASUREMENT_PATH" +
25-
" and copies coverage files from instruments directory.") {
21+
test("calling Invoker.invokedUseEnvironments puts the data on sys property scoverage_measurement_path") {
2622

2723
val testIds: Set[Int] = (1 to 10).toSet
2824

29-
testIds.map { i: Int => Invoker.invokedWriteToClasspath(i, instrumentsDir(i % 2).toString)}
25+
testIds.map { i: Int => Invoker.invokedWriteToClasspath(i, targetIds(i % 2).toString)}
3026

31-
// Verify measurements went to correct directory under the environment variable.
32-
val dir0 = s"${System.getenv("SCOVERAGE_MEASUREMENT_PATH")}/${Invoker.safe_name(instrumentsDir(0).toString)}"
27+
// Verify that [Invoker.dataDir] has been set correctly.
28+
assert(Invoker.dataDir == System.getProperty("scoverage_measurement_path"))
29+
30+
// Verify measurements went to correct directory under the system property.
31+
val dir0 = s"${Invoker.dataDir}/${targetIds(0).toString}"
3332
val measurementFiles3 = Invoker.findMeasurementFiles(dir0)
3433
val idsFromFile3 = Invoker.invoked(measurementFiles3.toIndexedSeq)
3534
assert (idsFromFile3 == testIds.filter { i: Int => i % 2 == 0})
3635

3736

38-
val dir1 = s"${System.getenv("SCOVERAGE_MEASUREMENT_PATH")}/${Invoker.safe_name(instrumentsDir(1).toString)}"
37+
val dir1 = s"${Invoker.dataDir}/${targetIds(1).toString}"
3938
val measurementFiles4 = Invoker.findMeasurementFiles(dir1)
4039
val idsFromFile4 = Invoker.invoked(measurementFiles4.toIndexedSeq)
4140
assert (idsFromFile4 == testIds.filter { i: Int => i % 2 == 1})
4241

43-
// Verify that coverage files have been copied correctly.
44-
assert(Files.exists(Paths.get(s"$dir0/scoverage.coverage")))
45-
assert(Files.exists(Paths.get(s"$dir1/scoverage.coverage")))
4642
}
4743

4844
after {
49-
deleteInstrumentFiles()
50-
instrumentsDir.foreach(_.delete())
5145
deleteMeasurementFolders()
5246
}
5347

54-
private def deleteInstrumentFiles(): Unit = {
55-
instrumentsDir.foreach((md) => {
56-
if (md.isDirectory)
57-
md.listFiles().foreach(_.delete())
58-
})
59-
}
6048

6149
private def deleteMeasurementFolders(): Unit = {
62-
val d = s"${System.getenv("SCOVERAGE_MEASUREMENT_PATH")}"
63-
instrumentsDir.foreach (i => {
64-
val f = new File(s"$d/${Invoker.safe_name(i.toString)}")
50+
val d = s"${Invoker.dataDir}"
51+
targetIds.foreach (i => {
52+
val f = new File(s"$d/${i.toString}")
6553
if (f.isDirectory)
6654
f.listFiles().foreach(_.delete())
6755
})
@@ -72,9 +60,5 @@ class InvokerUseEnvironmentTest extends FunSuite with BeforeAndAfter {
7260
f2.delete()
7361
}
7462

75-
private def createNewInstruments(): Unit = {
76-
new File("target/invoker-test.instrument0/scoverage.coverage").createNewFile()
77-
new File("target/invoker-test.instrument1/scoverage.coverage").createNewFile()
78-
}
7963

8064
}

0 commit comments

Comments
 (0)