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