forked from renaissance-benchmarks/renaissance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
build.sbt
255 lines (225 loc) · 9.31 KB
/
build.sbt
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import org.renaissance.License
import org.renaissance.core.Launcher
import scala.collection._
val renaissanceScalaVersion = "2.12.8"
lazy val renaissanceCore = RootProject(uri("renaissance-core"))
lazy val renaissanceHarness = RootProject(uri("renaissance-harness"))
val benchmarkProjects = for {
// Hint: add .filter(_ == "group") to compile with selected group only
// (can significantly speed-up compilation/assembly when debugging harness).
dir <- file("benchmarks").list()
if dir != null && file("benchmarks/" + dir + "/build.sbt").exists()
} yield {
RootProject(uri("benchmarks/" + dir))
}
val subProjects = benchmarkProjects :+ renaissanceHarness
val allProjects = subProjects :+ renaissanceCore
// Do not assemble fat JARs in subprojects
aggregate in assembly := false
def flattenTasks[A](tasks: Seq[Def.Initialize[Task[A]]]): Def.Initialize[Task[Seq[A]]] =
tasks.toList match {
case Nil => Def.task { Nil }
case x :: xs =>
Def.taskDyn {
flattenTasks(xs) map (x.value +: _)
}
}
def projectJars = Def.taskDyn {
// Each generated task returns tuple of
// (project path, all JAR files, all JAR files without renaissance core JAR*)
// * because renaissance core classes are shared across all benchmarks
val projectJarTasks = for {
p <- subProjects
} yield
Def.task {
val mainJar = (packageBin in (p, Compile)).value
val coreJar = (packageBin in (renaissanceCore, Runtime)).value
val depJars = (dependencyClasspath in (p, Compile)).value.map(_.data).filter(_.isFile)
val loadedJarFiles = mainJar +: depJars
val allJars = mainJar +: coreJar +: depJars
val project = p.asInstanceOf[RootProject].build.getPath
val jarFiles = for (jar <- allJars) yield {
val dest = (resourceManaged in Compile).value / project / jar.getName
dest.getParentFile.mkdirs()
Files.copy(jar.toPath, dest.toPath, StandardCopyOption.REPLACE_EXISTING)
dest
}
(project, jarFiles, loadedJarFiles)
}
flattenTasks(projectJarTasks)
}
def jarsAndListGenerator = Def.taskDyn {
val nonGpl = nonGplOnly.value
val logger = streams.value.log
// Flatten list and create a groups-jars file and the benchmark-details file.
projectJars.map { groupJars =>
// Add the benchmarks from the different project groups.
val jarListContent = new StringBuilder
val benchDetails = new java.util.Properties
for ((project, allJars, loadedJars) <- groupJars) {
val jarLine = loadedJars.map(jar => project + "/" + jar.getName).mkString(",")
val projectShort = project.stripPrefix("benchmarks/")
jarListContent.append(projectShort).append("=").append(jarLine).append("\n")
// Scan project jars for benchmarks and fill the property file.
for (benchInfo <- Benchmarks.listBenchmarks(allJars, Some(logger))) {
if (!nonGpl || benchInfo.distro() == License.MIT) {
for ((k, v) <- benchInfo.toMap()) {
benchDetails.setProperty(s"benchmark.${benchInfo.name()}.$k", v)
}
}
}
}
val jarListFile = (resourceManaged in Compile).value / "groups-jars.txt"
IO.write(jarListFile, jarListContent.toString, StandardCharsets.UTF_8)
val benchDetailsFile = (resourceManaged in Compile).value / "benchmark-details.properties"
val benchDetailsStream = new java.io.FileOutputStream(benchDetailsFile)
benchDetails.store(benchDetailsStream, "Benchmark details")
benchDetailsFile +: jarListFile +: groupJars.flatMap {
case (_, jars, _) => jars
}
}
}
addCommandAlias("renaissanceFormat", ";scalafmt;scalafmtSbt")
addCommandAlias("renaissanceFormatCheck", ";scalafmtCheck;scalafmtSbtCheck")
lazy val remoteDebug = SettingKey[Boolean](
"remoteDebug",
"Whether or not to enter a remote debugging session when running Renaissance."
)
lazy val nonGplOnly = SettingKey[Boolean](
"nonGplOnly",
"If set to true, then the distribution will not include GPL, EPL and MPL-licensed benchmarks."
)
val setupPrePush = taskKey[Unit](
"Installs git pre-push hook."
)
def startupTransition(state: State): State = {
"setupPrePush" :: state
}
// Installs a symlink to local pre-push hook.
def addLink(scriptFile: File, linkFile: File): Unit = {
val linkPath = linkFile.toPath
if (Files.isSymbolicLink(linkPath)) {
// If the link can be read/executed, ensure that
// it points to the desired pre-push hook script.
val scriptPath = scriptFile.toPath
if (Files.isExecutable(linkPath)) {
if (Files.isSameFile(linkPath.toRealPath(), scriptPath)) {
return
}
}
// Otherwise just force a new relative symlink.
val scriptRelPath = linkPath.getParent.relativize(scriptPath)
println(s"Setting pre-push hook link to $scriptRelPath")
Files.delete(linkPath)
Files.createSymbolicLink(linkPath, scriptRelPath)
} else {
println("Not installing pre-push hook link over existing regular file!")
}
}
lazy val renaissance: Project = {
val p = Project("renaissance", file("."))
.settings(
name := "renaissance",
version := (version in renaissanceCore).value,
organization := (organization in renaissanceCore).value,
crossPaths := false,
autoScalaLibrary := false,
resourceGenerators in Compile += jarsAndListGenerator.taskValue,
fork in run := true,
cancelable in Global := true,
remoteDebug := false,
nonGplOnly := false,
setupPrePush := addLink(file("tools") / "pre-push", file(".git") / "hooks" / "pre-push"),
packageOptions := Seq(
sbt.Package.ManifestAttributes(
("Specification-Title", "Renaissance Benchmark Suite"),
// Consider Specification-Version to mark sets of active benchmarks
("Git-Head-Commit", git.gitHeadCommit.value.get),
("Git-Head-Commit-Date", git.gitHeadCommitDate.value.get),
("Git-Uncommitted-Changes", git.gitUncommittedChanges.value.toString)
)
),
// Configure fat JAR: specify its name, main(), do not run tests when
// building it and raise error on file conflicts.
assemblyJarName in assembly :=
"renaissance-" + (if (nonGplOnly.value) "mit" else "gpl") +
"-" + (version in renaissanceCore).value + ".jar",
mainClass in assembly := Some(classOf[Launcher].getName),
test in assembly := {},
assemblyMergeStrategy in assembly := {
case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard
case _ => MergeStrategy.singleOrError
},
javaOptions in Compile ++= {
if (remoteDebug.value) {
Seq("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=8000")
} else {
Seq()
}
},
onLoad in Global := {
val old = (onLoad in Global).value
old.andThen(startupTransition)
}
)
.dependsOn(
renaissanceCore
)
allProjects.foldLeft(p)(_ aggregate _)
}
// This project generates a custom JMH wrapper for each Renaissance benchmark.
// Then, the project inserts JMH-specific functionality into the final JAR,
// which can then be run in a JMH-compliant way.
//
// Here are several technical details.
// First, to be able to generate the JMH wrapper classes, a project needs to "see"
// all the projects that define the benchmarks.
// Therefore, it was convenient to make the JMH project depend on the `renaissance` project.
// Second, the sbt-jmh plugins adds some (unnecessary) extra functionality,
// which forces this project to depend on the Scala library.
// This is why we cannot set `autoScalaLibrary` to `false` here.
// Third, the sbt-assembly plugin consequently automatically includes the Scala library
// into the fat JAR, which breaks classloading in the benchmarks that have
// a different Scala version (because the fat JAR is in the `AppClassLoader`).
// To amend this, we forcefully remove the Scala library classfiles from the fat JAR
// with a custom merge strategy -- this is fine, because the Scala library is only needed
// by the extra functionality that the sbt-jmh inserts into the artifact, and we don't
// need that anyway.
lazy val renaissanceJmh = (project in file("renaissance-jmh"))
.enablePlugins(JmhPlugin)
.settings(
name := "renaissance-jmh",
version := (version in renaissance).value,
organization := (organization in renaissance).value,
scalafmtConfig := Some(file(".scalafmt.conf")),
nonGplOnly := (nonGplOnly in renaissance).value,
mainClass in assembly := Some("org.openjdk.jmh.Main"),
sourceGenerators in Compile := Def.taskDyn {
val log = streams.value.log
val outputDir = sourceManaged.in(Compile).value
val nonGpl = nonGplOnly.value
projectJars.map { groupJars =>
RenaissanceJmh.generateJmhWrapperBenchmarkClasses(
outputDir,
log,
nonGpl,
groupJars
)
}
}.taskValue +: (sourceGenerators in Compile).value,
assembly in Jmh := ((assembly in Jmh) dependsOn (compile in Jmh)).value,
assemblyMergeStrategy in assembly := {
case PathList("scala", _*) =>
MergeStrategy.discard
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
)
.dependsOn(
renaissance
)
Project.inConfig(Jmh)(baseAssemblySettings)