1
+ package scala .tools .nsc
2
+
3
+ import java .io ._
4
+ import java .net .URL
5
+ import java .nio .file ._
6
+ import java .nio .file .attribute .BasicFileAttributes
7
+ import java .util .concurrent .TimeUnit
8
+
9
+ import com .typesafe .config .ConfigFactory
10
+ import org .openjdk .jmh .annotations .Mode .SampleTime
11
+ import org .openjdk .jmh .annotations ._
12
+
13
+ import scala .collection .JavaConverters ._
14
+
15
+ @ State (Scope .Benchmark )
16
+ @ BenchmarkMode (Array (SampleTime ))
17
+ @ OutputTimeUnit (TimeUnit .MILLISECONDS )
18
+ @ Warmup (iterations = 10 , time = 10 , timeUnit = TimeUnit .SECONDS )
19
+ @ Measurement (iterations = 10 , time = 10 , timeUnit = TimeUnit .SECONDS )
20
+ @ Fork (value = 3 )
21
+ class HotSbtBenchmark {
22
+ @ Param (value = Array ())
23
+ var source : String = _
24
+
25
+ @ Param (value = Array (" " ))
26
+ var extraArgs : String = _
27
+
28
+ // This parameter is set by ScalacBenchmarkRunner / UploadingRunner based on the Scala version.
29
+ // When running the benchmark directly the "latest" symlink is used.
30
+ @ Param (value = Array (" latest" ))
31
+ var corpusVersion : String = _
32
+
33
+ var sbtProcess : Process = _
34
+ var inputRedirect : ProcessBuilder .Redirect = _
35
+ var outputRedirect : ProcessBuilder .Redirect = _
36
+ var tempDir : Path = _
37
+ var scalaHome : Path = _
38
+ var processOutputReader : BufferedReader = _
39
+ var processInputReader : BufferedWriter = _
40
+ var output = new java.lang.StringBuilder ()
41
+
42
+ def buildDef =
43
+ s """
44
+ |scalaHome := Some(file(" ${scalaHome.toAbsolutePath.toString}"))
45
+ |
46
+ |val cleanClasses = taskKey[Unit]("clean the classes directory")
47
+ |
48
+ |cleanClasses := IO.delete((classDirectory in Compile).value)
49
+ |
50
+ |scalaSource in Compile := file(" ${corpusSourcePath.toAbsolutePath.toString}")
51
+ |
52
+ |libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value
53
+ |libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
54
+ |
55
+ |// TODO support .java sources
56
+ """ .stripMargin
57
+
58
+ @ Setup (Level .Trial ) def spawn (): Unit = {
59
+ tempDir = Files .createTempDirectory(" sbt-" )
60
+ scalaHome = Files .createTempDirectory(" scalaHome-" )
61
+ initDepsClasspath()
62
+ Files .createDirectory(tempDir.resolve(" project" ))
63
+ Files .write(tempDir.resolve(" project/build.properties" ), java.util.Arrays .asList(" sbt.version=0.13.15" ))
64
+ Files .write(tempDir.resolve(" build.sbt" ), buildDef.getBytes(" UTF-8" ))
65
+ val sbtLaucherPath = System .getProperty(" sbt.launcher" )
66
+ if (sbtLaucherPath == null ) sys.error(" System property -Dsbt.launcher absent" )
67
+ val builder = new ProcessBuilder (sys.props(" java.home" ) + " /bin/java" , " -Xms2G" , " -Xmx2G" , " -Dsbt.log.format=false" , " -jar" , sbtLaucherPath)
68
+ builder.directory(tempDir.toFile)
69
+ inputRedirect = builder.redirectInput()
70
+ outputRedirect = builder.redirectOutput()
71
+ sbtProcess = builder.start()
72
+ processOutputReader = new BufferedReader (new InputStreamReader (sbtProcess.getInputStream))
73
+ processInputReader = new BufferedWriter (new OutputStreamWriter (sbtProcess.getOutputStream))
74
+ awaitPrompt()
75
+ }
76
+
77
+ @ Benchmark
78
+ def compile (): Unit = {
79
+ issue(" ;cleanClasses;compile" )
80
+ awaitPrompt()
81
+ }
82
+
83
+ def issue (str : String ) = {
84
+ processInputReader.write(str + " \n " )
85
+ processInputReader.flush()
86
+ }
87
+
88
+ def awaitPrompt (): Unit = {
89
+ output.setLength(0 )
90
+ var line = " "
91
+ val buffer = new Array [Char ](128 )
92
+ var read : Int = - 1
93
+ while (true ) {
94
+ read = processOutputReader.read(buffer)
95
+ if (read == - 1 ) sys.error(" EOF" )
96
+ else {
97
+ output.append(buffer, 0 , read)
98
+ if (output.toString.contains(" \n > " )) {
99
+ if (output.toString.contains(" [error" )) sys.error(output.toString)
100
+ return
101
+ }
102
+ }
103
+ }
104
+
105
+ }
106
+
107
+ private def corpusSourcePath = Paths .get(s " ../corpus/ $source/ $corpusVersion" )
108
+
109
+ def initDepsClasspath (): Unit = {
110
+ val libDir = tempDir.resolve(" lib" )
111
+ Files .createDirectories(libDir)
112
+ for (depFile <- BenchmarkUtils .initDeps(corpusSourcePath)) {
113
+ val libDirFile = libDir.resolve(depFile.getFileName)
114
+ Files .copy(depFile, libDir)
115
+ }
116
+
117
+ val scalaHomeLibDir = scalaHome.resolve(" lib" )
118
+ Files .createDirectories(scalaHomeLibDir)
119
+ for (elem <- sys.props(" java.class.path" ).split(File .pathSeparatorChar)) {
120
+ val jarFile = Paths .get(elem)
121
+ var name = jarFile.getFileName.toString
122
+ if (name.startsWith(" scala" ) && name.endsWith(" .jar" )) {
123
+ if (name.startsWith(" scala-library" ))
124
+ name = " scala-library.jar"
125
+ else if (name.startsWith(" scala-reflect" ))
126
+ name = " scala-reflect.jar"
127
+ else if (name.startsWith(" scala-compiler" ))
128
+ name = " scala-compiler.jar"
129
+ Files .copy(jarFile, scalaHomeLibDir.resolve(name))
130
+ }
131
+ }
132
+
133
+ }
134
+
135
+ @ TearDown (Level .Trial ) def terminate (): Unit = {
136
+ processOutputReader.close()
137
+ sbtProcess.destroyForcibly()
138
+ BenchmarkUtils .deleteRecursive(tempDir)
139
+ BenchmarkUtils .deleteRecursive(scalaHome)
140
+ }
141
+ }
0 commit comments