-
Notifications
You must be signed in to change notification settings - Fork 29
/
ScctPlugin.scala
134 lines (110 loc) · 5.39 KB
/
ScctPlugin.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import java.util.Properties
import sbt._
import sbt.Keys._
import xml.transform._
object ScctPlugin extends Plugin {
val scctReportDir = SettingKey[File]("scct-report-dir")
lazy val Scct = config("scct")
lazy val ScctTest = config("scct-test") extend Scct
lazy val instrumentSettings =
inConfig(Scct)(Defaults.compileSettings) ++
inConfig(ScctTest)(Defaults.testSettings) ++
Seq(
scctReportDir <<= crossTarget / "coverage-report",
ivyConfigurations ++= Seq(Scct, ScctTest),
// Local dev:
// resolvers += "scct-repository" at "file:///Users/mtkopone/dev/scct-root/gh-pages/maven-repo",
// Actual usage:
resolvers += "scct-repository" at "http://mtkopone.github.com/scct/maven-repo",
libraryDependencies += "reaktor" %% "scct" % "0.2-SNAPSHOT" % "scct",
sources in Scct <<= (sources in Compile),
sourceDirectory in Scct <<= (sourceDirectory in Compile),
scalacOptions in Scct <++= (name in Scct, baseDirectory in Scct, update) map { (n, b, report) =>
val pluginClasspath = report matching configurationFilter("scct")
if (pluginClasspath.isEmpty) throw new Exception("Fatal: scct not in libraryDependencies. Use e.g. <+= or <++= instead of <<=")
Seq(
"-Xplugin:" + pluginClasspath.head.getAbsolutePath,
"-P:scct:projectId:" + n,
"-P:scct:basedir:" + b
)
},
sources in ScctTest <<= (sources in Test),
sourceDirectory in ScctTest <<= (sourceDirectory in Test),
externalDependencyClasspath in Scct <<= Classpaths.concat(externalDependencyClasspath in Scct, externalDependencyClasspath in Compile),
externalDependencyClasspath in ScctTest <<= Classpaths.concat(externalDependencyClasspath in ScctTest, externalDependencyClasspath in Test),
internalDependencyClasspath in Scct <<= (internalDependencyClasspath in Compile),
internalDependencyClasspath in ScctTest <<= (internalDependencyClasspath in Test, internalDependencyClasspath in ScctTest, classDirectory in Compile) map { (testDeps, scctDeps, oldClassDir) =>
scctDeps ++ testDeps.filter(_.data != oldClassDir)
},
testOptions in ScctTest <+= (name in Scct, baseDirectory in Scct, scalaSource in Scct, classDirectory in ScctTest, scctReportDir) map { (n, base, src, testClassesDir, reportDir) =>
Tests.Setup { () =>
val props = new Properties()
props.setProperty("scct.basedir", base.getAbsolutePath)
props.setProperty("scct.report.hook", "system.property")
props.setProperty("scct.project.name", n)
props.setProperty("scct.report.dir", reportDir.getAbsolutePath)
props.setProperty("scct.source.dir", src.getAbsolutePath)
val out = testClassesDir / "scct.properties"
IO.write(props, "Env for scct test run and report generation", out)
}
},
testOptions in ScctTest <+= (state, name in Scct) map { (s, n) =>
Tests.Cleanup { () =>
val reportProperty = "scct.%s.fire.report".format(n)
System.setProperty(reportProperty, "true")
val maxSleep = compat.Platform.currentTime + 60L*1000L
while (sys.props(reportProperty) != "done" && compat.Platform.currentTime < maxSleep) Thread.sleep(200L)
if (sys.props(reportProperty) != "done") println("scct: ["+n+"] Timed out waiting for coverage report.")
}
},
// Sugar: copy test from ScctTest to Scct so you can use scct:test
Keys.test in Scct <<= (Keys.test in ScctTest),
// filter scct dependencies for publishing
pomPostProcess := { (node: xml.Node) => pomTransformer(node) }
)
val pomTransformer = new RuleTransformer(new RewriteRule {
override def transform(node: xml.Node): Seq[xml.Node] = node match {
case e: xml.Elem if e.label == "dependency" =>
if ((e \ "scope" text) == "scct") Nil else Seq(e)
case e: xml.Elem if e.label == "repository" =>
if ((e \ "name" text) == "scct-repository") Nil else Seq(e)
case e => Seq(e)
}
})
def scctJarPath = {
val url = classOf[reaktor.scct.ScctInstrumentPlugin].getProtectionDomain().getCodeSource().getLocation()
new File(url.toURI).getAbsolutePath
}
// Report mergin':
object ScctMergeReportKeys {
val sourceFiles = TaskKey[Seq[File]]("scct-merge-report-source-files")
val merge = TaskKey[File]("scct-merge-report")
}
import ScctMergeReportKeys._
val mergeReportSettings = Seq(
scctReportDir <<= crossTarget / "coverage-report",
sourceFiles <<= (thisProjectRef, buildStructure) map coverageResultFiles,
merge <<= (sourceFiles, scctReportDir) map generateReport
)
def coverageResultFiles(projectRef: ProjectRef, structure: Load.BuildStructure) = {
val projects = aggregated(projectRef, structure)
projects flatMap { p =>
val dir = (scctReportDir in ScctTest in LocalProject(p)).get(structure.data)
dir.flatMap { d =>
val f = new File(d, "coverage-result.data")
if (f.exists) Some(f) else None
}
}
}
def generateReport(input: Seq[File], out: File) = {
import reaktor.scct.report._
MultiProjectHtmlReporter.report(input, out)
out
}
def aggregated(projectRef: ProjectRef, structure: Load.BuildStructure): Seq[String] = {
val aggregate = Project.getProject(projectRef, structure).toSeq.flatMap(_.aggregate)
aggregate flatMap { ref =>
ref.project +: aggregated(ref, structure)
}
}
}