Skip to content

Commit

Permalink
move all to plugin and add disclaimer
Browse files Browse the repository at this point in the history
  • Loading branch information
ktoso committed May 20, 2014
1 parent f2d3323 commit b4d7c7f
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 87 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
sbt-jmh
=======

**THIS IS AN EARLY DRAFT - THOUGH IT WORKS, IT IS CURRENTLY NOT PROPERLY MAINTAINED AS PLUGIN **

Sbt plugin for running JMH tests.

http://openjdk.java.net/projects/code-tools/jmh/
Expand Down Expand Up @@ -121,4 +123,4 @@ The examples are scala-fied examples from tethe original JMH repo, check them ou
License
-------

This plugin is reeased under the **Apache 2.0 License**
This plugin is reeased under the **Apache 2.0 License**
74 changes: 3 additions & 71 deletions example-project/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -11,76 +11,8 @@ import inc._

jmhSettings

def myCompileTask: Initialize[Task[inc.Analysis]] = Def.task {
myCompileTaskImpl(streams.value, (compileInputs in compile).value)
}
//def myCompileTask: Initialize[Task[inc.Analysis]] = Def.task {
// myCompileTaskImpl(streams.value, (compileInputs in compile).value)
//}

def exported(w: PrintWriter, command: String): Seq[String] => Unit = args =>
w.println((command +: args).mkString(" "))

def exported(s: TaskStreams, command: String): Seq[String] => Unit = args => {
val w = s.text(ExportStream)
try exported(w, command)
finally w.close() // workaround for #937
}


def myCompileTaskImpl(s: TaskStreams, ci: Compiler.Inputs): inc.Analysis = {
lazy val x = s.text(ExportStream)
def onArgs(cs: Compiler.Compilers) = {
cs.copy(
scalac = cs.scalac.onArgs(exported(x, "scalac")),
javac = cs.javac.onArgs(exported(x, "javac")))
}
val i = ci.copy(compilers = onArgs(ci.compilers))
try Compiler(i, s.log) finally x.close() // workaround for #937
}

def myCompileGeneratedJava(s: TaskStreams, ci: Compiler.Inputs, javaToCompile: Seq[File]): inc.Analysis = {
lazy val x = s.text(ExportStream)
def onArgs(cs: Compiler.Compilers) = {
cs.copy(
scalac = cs.scalac.onArgs(exported(x, "scalac")),
javac = cs.javac.onArgs(exported(x, "javac")))
}
val i = ci
.copy(compilers = onArgs(ci.compilers))
.copy(config = ci.config.copy(sources = ci.config.sources ++ javaToCompile))
try Compiler(i, s.log) finally x.close() // workaround for #937
}

generateJavaSources in Jmh := {
streams.value.log.info("Generating sources.............")
val out = (target in Compile).value / s"scala-${scalaBinaryVersion.value}"
val compiledBytecodeDirectory = out / "classes"
val outputSourceDirectory = out / "generated-sources" / "jmh"
val outputResourceDirectory = compiledBytecodeDirectory
val micro = classOf[GenerateMicroBenchmark]
Thread.currentThread().setContextClassLoader(micro.getClassLoader)
JmhBytecodeGenerator.main(Array(compiledBytecodeDirectory, outputSourceDirectory, outputResourceDirectory).map(_.toString))
(outputSourceDirectory ** "*").filter(_.isFile).get
}

compile in Jmh := {
streams.value.log.info("Compiling AGAIN from JMH.............")
val generatedJava = (generateJavaSources in Jmh).value
myCompileGeneratedJava(streams.value, (compileInputs in (Compile, compile)).value, generatedJava)
}

compileAgain in Jmh := {
streams.value.log.info("Compiling from JMH.............")
myCompileTaskImpl(streams.value, (compileInputs in (Compile, compile)).value)
}

sourceDirectories in Compile += target.value / s"scala-${scalaBinaryVersion.value}" / "generated-sources" / "jmh"

compile in Jmh <<= (compile in Jmh).dependsOn(generateJavaSources in Jmh, compile in Compile)

compile in Jmh <<= (compile in Jmh).dependsOn(compileAgain in Jmh)

compile in Jmh <<= (compile in Jmh).dependsOn(compile in Compile)


run in Jmh <<= (run in Compile).dependsOn(compile in Jmh)

run in Compile <<= (run in Compile).dependsOn(compile in Jmh)
106 changes: 91 additions & 15 deletions src/main/scala/com/typesafe/sbt/SbtJmh.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.typesafe.sbt

import sbt._
import Keys._
import sbt.Keys._
import org.openjdk.jmh.annotations.GenerateMicroBenchmark
import org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator
import sbt.inc.Analysis
import scala.Some
import scala.Some
import sbt.CommandStrings._
import scala.Some
import java.io.PrintWriter

object SbtJmh extends Plugin {

Expand All @@ -19,36 +24,107 @@ object SbtJmh extends Plugin {

fork in (Compile, run) := true, // makes sure that sbt manages classpath for JMH when forking

generateJavaSources in Compile := {
val out = (target in Compile).value / s"scala-${scalaBinaryVersion.value}"

val compiledBytecodeDirectory = out / "classes"
val outputSourceDirectory = out / "generated-sources" / "jmh"
val outputResourceDirectory = compiledBytecodeDirectory

val micro = classOf[GenerateMicroBenchmark]
Thread.currentThread().setContextClassLoader(micro.getClassLoader)

JmhBytecodeGenerator.main(Array(compiledBytecodeDirectory, outputSourceDirectory, outputResourceDirectory).map(_.toString))
// generateJavaSources in Compile := {
// val out = (target in Compile).value / s"scala-${scalaBinaryVersion.value}"
//
// val compiledBytecodeDirectory = out / "classes"
// val outputSourceDirectory = out / "generated-sources" / "jmh"
// val outputResourceDirectory = compiledBytecodeDirectory
//
// val micro = classOf[GenerateMicroBenchmark]
// Thread.currentThread().setContextClassLoader(micro.getClassLoader)
//
// JmhBytecodeGenerator.main(Array(compiledBytecodeDirectory, outputSourceDirectory, outputResourceDirectory).map(_.toString))
//
// (outputSourceDirectory ** "*").filter(_.isFile).get
// },

sourceDirectories in Compile += target.value / s"scala-${scalaBinaryVersion.value}" / "generated-sources" / "jmh",

This comment has been minimized.

Copy link
@jsuereth

jsuereth Jun 25, 2014

Member

This looks prone to errors later. Combined with compileAgain.

  1. You do NOT want JMH code ending up in the JAR file you distribute
  2. If a JMH class winds up in the list of sources, but is bad, it'll fail the compile with no possible resolution.

What you want to do:

  1. Duplicate (copy?) the sources task into Jmh. Add this to the sources in Jmh.
  2. Leave generate sources only in Jmh
  3. Duplicate compile task in Jmh, so that it only compiles the new Jmh files (use the classDirectories/classpath of compile, rather than depending on it directly, something like fullClasspath in Compile should be sufficient to ensure compile is run).

However, I guess a few questions before that direction:

  1. Do you need to recompile everything or just the new java sources?
  2. Your generateJavaSources in Jmh task (currently in Compile) should actually depend on the OUTPUT of compile so that you can guarantee the class files exist. Did you run into a "loop" exception in tasks? you should have, and that points to the fundamental issue.

This comment has been minimized.

Copy link
@ktoso

ktoso Jun 26, 2014

Author Member

Thanks for reviewing Josh!

Agreed on the comments, will rework it as soon as I'm married (in a few days) :-)

However - I will want to ship JMH classes in a jar. The story is: you create a benchmarks.jar which you ship to your prod machine, and run your benchmarks on there (or run the same benchmarks jar on different machines and compare results).

Q1) just the generated java sources - I'm not sure how to achieve this though. Steps are: compile scala, look at bytecode to generate java sources, compile java sources, run on the bytecode from these java sources.
Q2) Hm, yes makes sense.


generateJavaSources in Jmh := generateBenchmarkJavaSources(streams.value, (target in Compile).value, scalaBinaryVersion.value),
generateJavaSources in Compile := generateBenchmarkJavaSources(streams.value, (target in Compile).value, scalaBinaryVersion.value),

compileAgain in Jmh := {
myCompileTaskImpl(streams.value, (compileInputs in (Compile, compile)).value)
},

(outputSourceDirectory ** "*").filter(_.isFile).get
compile in Jmh := {
streams.value.log.info("Compiling generated JMH benchmarks...")
val generatedJava = (generateJavaSources in Jmh).value
myCompileGeneratedJava(streams.value, (compileInputs in (Compile, compile)).value, generatedJava)
},

libraryDependencies ++= Seq(
"org.openjdk.jmh" % "jmh-core" % "0.7.1", // GPLv2
"org.openjdk.jmh" % "jmh-generator-bytecode" % "0.7.1", // GPLv2
"org.openjdk.jmh" % "jmh-generator-reflection" % "0.7.1" // GPLv2
)
),

compile in Jmh <<= (compile in Jmh).dependsOn(generateJavaSources in Jmh, compile in Compile),
compile in Jmh <<= (compile in Jmh).dependsOn(compileAgain in Jmh),
compile in Jmh <<= (compile in Jmh).dependsOn(compile in Compile),

run in Jmh <<= (run in Compile).dependsOn(compile in Jmh),
run in Compile <<= (run in Compile).dependsOn(compile in Jmh)
)



def generateBenchmarkJavaSources(s: TaskStreams, target: File, scalaBinaryV: String): Seq[File] = {
s.log.info("Generating JMH benchmark Java source files...")

val out = target / s"scala-$scalaBinaryV"

val compiledBytecodeDirectory = out / "classes"
val outputSourceDirectory = out / "generated-sources" / "jmh"
val outputResourceDirectory = compiledBytecodeDirectory

// assuring the classes are loaded in case of same thread execution
val micro = classOf[GenerateMicroBenchmark]
Thread.currentThread().setContextClassLoader(micro.getClassLoader)

JmhBytecodeGenerator.main(Array(compiledBytecodeDirectory, outputSourceDirectory, outputResourceDirectory).map(_.toString))
(outputSourceDirectory ** "*").filter(_.isFile).get
}

/** Compiler run, with additional files to compile (JMH generated sources) */
def myCompileGeneratedJava(s: TaskStreams, ci: Compiler.Inputs, javaToCompile: Seq[File]): inc.Analysis = {
lazy val x = s.text(ExportStream)
def onArgs(cs: Compiler.Compilers) = {
cs.copy(
scalac = cs.scalac.onArgs(exported(x, "scalac")),
javac = cs.javac.onArgs(exported(x, "javac")))
}
val i = ci
.copy(compilers = onArgs(ci.compilers))
.copy(config = ci.config.copy(sources = ci.config.sources ++ javaToCompile))
try Compiler(i, s.log) finally x.close() // workaround for #937
}

def myCompileTaskImpl(s: TaskStreams, ci: Compiler.Inputs): inc.Analysis = {
lazy val x = s.text(ExportStream)
def onArgs(cs: Compiler.Compilers) = {
cs.copy(
scalac = cs.scalac.onArgs(exported(x, "scalac")),
javac = cs.javac.onArgs(exported(x, "javac")))
}
val i = ci.copy(compilers = onArgs(ci.compilers))
try Compiler(i, s.log) finally x.close() // workaround for #937
}

def exported(w: PrintWriter, command: String): Seq[String] => Unit = args =>
w.println((command +: args).mkString(" "))


object JmhKeys {
val Jmh = config("jmh") extend Compile

val generateJavaSources = taskKey[Seq[File]]("Generate benchmark JMH Java code")

val generateInstrumentedClasses = taskKey[Seq[File]]("Generate instrumented JMH code")

val compileAgain = taskKey[Analysis]("Compile the generated sources")

}

}

0 comments on commit b4d7c7f

Please sign in to comment.