Skip to content

Commit

Permalink
Add using directives for benchmarking with JMH
Browse files Browse the repository at this point in the history
  • Loading branch information
Gedochao committed Aug 21, 2024
1 parent ccd1ceb commit 974ebb8
Show file tree
Hide file tree
Showing 14 changed files with 388 additions and 258 deletions.
5 changes: 5 additions & 0 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,11 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests
|
| def ghOrg = "$ghOrg"
| def ghName = "$ghName"
|
| def jmhVersion = "${Deps.Versions.jmh}"
| def jmhOrg = "${Deps.jmhCore.dep.module.organization.value}"
| def jmhCoreModule = "${Deps.jmhCore.dep.module.name.value}"
| def jmhGeneratorBytecodeModule = "${Deps.jmhGeneratorBytecode.dep.module.name.value}"
|}
|""".stripMargin
if (!os.isFile(dest) || os.read(dest) != code)
Expand Down
2 changes: 1 addition & 1 deletion modules/build/src/main/scala/scala/build/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ object Build {

build0 match {
case successful: Successful =>
if (options.jmhOptions.runJmh.getOrElse(false) && scope == Scope.Main)
if (options.jmhOptions.canRunJmh && scope == Scope.Main)
value {
val res = jmhBuild(
inputs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import scala.build.preprocessing.directives
object DirectivesPreprocessingUtils {
val usingDirectiveHandlers: Seq[DirectiveHandler[BuildOptions]] =
Seq[DirectiveHandler[_ <: HasBuildOptions]](
directives.Benchmarking.handler,
directives.BuildInfo.handler,
directives.ComputeVersion.handler,
directives.Exclude.handler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ object Bloop extends ScalaCommand[BloopOptions] {
jvm = opts.jvm,
coursier = opts.coursier
)
val options = sharedOptions.buildOptions(false, None).orExit(opts.global.logging.logger)
val options = sharedOptions.buildOptions().orExit(opts.global.logging.logger)

val javaHomeInfo = opts.compilationServer.bloopJvm
.map(JvmUtils.downloadJvm(_, options))
Expand Down
7 changes: 1 addition & 6 deletions modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,7 @@ object Doc extends ScalaCommand[DocOptions] {

override def buildOptions(options: DocOptions): Option[BuildOptions] =
sharedOptions(options)
.map(shared =>
shared.buildOptions(
enableJmh = shared.benchmarking.jmh.getOrElse(false),
jmhVersion = shared.benchmarking.jmhVersion
).orExit(shared.logger)
)
.map(shared => shared.buildOptions().orExit(shared.logger))

override def helpFormat: HelpFormat = super.helpFormat.withPrimaryGroup(HelpGroup.Doc)

Expand Down
10 changes: 4 additions & 6 deletions modules/cli/src/main/scala/scala/cli/commands/run/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
override def buildOptions(options: RunOptions): Some[BuildOptions] = Some {
import options.*
import options.sharedRun.*
val logger = options.shared.logger
val baseOptions = shared.buildOptions(
enableJmh = shared.benchmarking.jmh.contains(true),
jmhVersion = shared.benchmarking.jmhVersion
).orExit(logger)
val logger = options.shared.logger
val baseOptions = shared.buildOptions().orExit(logger)
baseOptions.copy(
mainClass = mainClass.mainClass,
javaOptions = baseOptions.javaOptions.copy(
Expand Down Expand Up @@ -375,7 +372,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {

val mainClassOpt = build.options.mainClass.filter(_.nonEmpty) // trim it too?
.orElse {
if (build.options.jmhOptions.runJmh.contains(false)) Some("org.openjdk.jmh.Main")
if build.options.jmhOptions.enableJmh.contains(true) && !build.options.jmhOptions.canRunJmh
then Some("org.openjdk.jmh.Main")
else None
}
val mainClass = mainClassOpt match {
Expand Down
290 changes: 138 additions & 152 deletions modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -301,152 +301,148 @@ final case class SharedOptions(

def scalacOptions: List[String] = scalac.scalacOption ++ scalacOptionsFromFiles

def buildOptions(
enableJmh: Boolean = false,
jmhVersion: Option[String] = None,
ignoreErrors: Boolean = false
): Either[BuildException, bo.BuildOptions] = either {
val releaseOpt = scalacOptions.getScalacOption("-release")
val targetOpt = scalacOptions.getScalacPrefixOption("-target")
jvm.jvm -> (releaseOpt.toSeq ++ targetOpt) match {
case (Some(j), compilerTargets) if compilerTargets.exists(_ != j) =>
val compilerTargetsString = compilerTargets.distinct.mkString(", ")
logger.error(
s"Warning: different target JVM ($j) and scala compiler target JVM ($compilerTargetsString) were passed."
)
case _ =>
}
val parsedPlatform = platform.map(Platform.normalize).flatMap(Platform.parse)
val platformOpt = value {
(parsedPlatform, js.js, native.native) match {
case (Some(p: Platform.JS.type), _, false) => Right(Some(p))
case (Some(p: Platform.Native.type), false, _) => Right(Some(p))
case (Some(p: Platform.JVM.type), false, false) => Right(Some(p))
case (Some(p), _, _) =>
val jsSeq = if (js.js) Seq(Platform.JS) else Seq.empty
val nativeSeq = if (native.native) Seq(Platform.Native) else Seq.empty
val platformsSeq = Seq(p) ++ jsSeq ++ nativeSeq
Left(new AmbiguousPlatformError(platformsSeq.distinct.map(_.toString)))
case (_, true, true) =>
Left(new AmbiguousPlatformError(Seq(Platform.JS.toString, Platform.Native.toString)))
case (_, true, _) => Right(Some(Platform.JS))
case (_, _, true) => Right(Some(Platform.Native))
case _ => Right(None)
def buildOptions(ignoreErrors: Boolean = false): Either[BuildException, bo.BuildOptions] =
either {
val releaseOpt = scalacOptions.getScalacOption("-release")
val targetOpt = scalacOptions.getScalacPrefixOption("-target")
jvm.jvm -> (releaseOpt.toSeq ++ targetOpt) match {
case (Some(j), compilerTargets) if compilerTargets.exists(_ != j) =>
val compilerTargetsString = compilerTargets.distinct.mkString(", ")
logger.error(
s"Warning: different target JVM ($j) and scala compiler target JVM ($compilerTargetsString) were passed."
)
case _ =>
}
}
val (assumedSourceJars, extraRegularJarsAndClasspath) =
extraJarsAndClassPath.partition(_.hasSourceJarSuffix)
if assumedSourceJars.nonEmpty then
val assumedSourceJarsString = assumedSourceJars.mkString(", ")
logger.message(
s"""[${Console.YELLOW}warn${Console.RESET}] Jars with the ${ScalaCliConsole
.GRAY}*-sources.jar${Console.RESET} name suffix are assumed to be source jars.
|The following jars were assumed to be source jars and will be treated as such: $assumedSourceJarsString""".stripMargin
)
val (resolvedToolkitDependency, toolkitMaxDefaultScalaNativeVersions) =
SharedOptions.resolveToolkitDependencyAndScalaNativeVersionReqs(withToolkit, logger)
val scalapyMaxDefaultScalaNativeVersions =
if sharedPython.python.contains(true) then
List(Constants.scalaPyMaxScalaNative -> Python.maxScalaNativeWarningMsg)
else Nil
val maxDefaultScalaNativeVersions =
toolkitMaxDefaultScalaNativeVersions.toList ++ scalapyMaxDefaultScalaNativeVersions
val snOpts = scalaNativeOptions(native, maxDefaultScalaNativeVersions)
bo.BuildOptions(
sourceGeneratorOptions = bo.SourceGeneratorOptions(
useBuildInfo = sourceGenerator.useBuildInfo,
projectVersion = sharedVersionOptions.projectVersion,
computeVersion = value {
sharedVersionOptions.computeVersion
.map(Positioned.commandLine)
.map(ComputeVersion.parse)
.sequence
val parsedPlatform = platform.map(Platform.normalize).flatMap(Platform.parse)
val platformOpt = value {
(parsedPlatform, js.js, native.native) match {
case (Some(p: Platform.JS.type), _, false) => Right(Some(p))
case (Some(p: Platform.Native.type), false, _) => Right(Some(p))
case (Some(p: Platform.JVM.type), false, false) => Right(Some(p))
case (Some(p), _, _) =>
val jsSeq = if (js.js) Seq(Platform.JS) else Seq.empty
val nativeSeq = if (native.native) Seq(Platform.Native) else Seq.empty
val platformsSeq = Seq(p) ++ jsSeq ++ nativeSeq
Left(new AmbiguousPlatformError(platformsSeq.distinct.map(_.toString)))
case (_, true, true) =>
Left(new AmbiguousPlatformError(Seq(Platform.JS.toString, Platform.Native.toString)))
case (_, true, _) => Right(Some(Platform.JS))
case (_, _, true) => Right(Some(Platform.Native))
case _ => Right(None)
}
),
suppressWarningOptions =
bo.SuppressWarningOptions(
suppressDirectivesInMultipleFilesWarning = getOptionOrFromConfig(
suppress.suppressDirectivesInMultipleFilesWarning,
Keys.suppressDirectivesInMultipleFilesWarning
}
val (assumedSourceJars, extraRegularJarsAndClasspath) =
extraJarsAndClassPath.partition(_.hasSourceJarSuffix)
if assumedSourceJars.nonEmpty then
val assumedSourceJarsString = assumedSourceJars.mkString(", ")
logger.message(
s"""[${Console.YELLOW}warn${Console.RESET}] Jars with the ${ScalaCliConsole
.GRAY}*-sources.jar${Console.RESET} name suffix are assumed to be source jars.
|The following jars were assumed to be source jars and will be treated as such: $assumedSourceJarsString""".stripMargin
)
val (resolvedToolkitDependency, toolkitMaxDefaultScalaNativeVersions) =
SharedOptions.resolveToolkitDependencyAndScalaNativeVersionReqs(withToolkit, logger)
val scalapyMaxDefaultScalaNativeVersions =
if sharedPython.python.contains(true) then
List(Constants.scalaPyMaxScalaNative -> Python.maxScalaNativeWarningMsg)
else Nil
val maxDefaultScalaNativeVersions =
toolkitMaxDefaultScalaNativeVersions.toList ++ scalapyMaxDefaultScalaNativeVersions
val snOpts = scalaNativeOptions(native, maxDefaultScalaNativeVersions)
bo.BuildOptions(
sourceGeneratorOptions = bo.SourceGeneratorOptions(
useBuildInfo = sourceGenerator.useBuildInfo,
projectVersion = sharedVersionOptions.projectVersion,
computeVersion = value {
sharedVersionOptions.computeVersion
.map(Positioned.commandLine)
.map(ComputeVersion.parse)
.sequence
}
),
suppressWarningOptions =
bo.SuppressWarningOptions(
suppressDirectivesInMultipleFilesWarning = getOptionOrFromConfig(
suppress.suppressDirectivesInMultipleFilesWarning,
Keys.suppressDirectivesInMultipleFilesWarning
),
suppressOutdatedDependencyWarning = getOptionOrFromConfig(
suppress.suppressOutdatedDependencyWarning,
Keys.suppressOutdatedDependenciessWarning
),
suppressExperimentalFeatureWarning = getOptionOrFromConfig(
suppress.global.suppressExperimentalFeatureWarning,
Keys.suppressExperimentalFeatureWarning
)
),
suppressOutdatedDependencyWarning = getOptionOrFromConfig(
suppress.suppressOutdatedDependencyWarning,
Keys.suppressOutdatedDependenciessWarning
scalaOptions = bo.ScalaOptions(
scalaVersion = scalaVersion
.map(_.trim)
.filter(_.nonEmpty)
.map(bo.MaybeScalaVersion(_)),
scalaBinaryVersion = scalaBinaryVersion.map(_.trim).filter(_.nonEmpty),
addScalaLibrary = scalaLibrary.orElse(java.map(!_)),
addScalaCompiler = withCompiler,
semanticDbOptions = bo.SemanticDbOptions(
generateSemanticDbs = semanticDbOptions.semanticDb,
semanticDbTargetRoot = semanticDbOptions.semanticDbTargetRoot.map(os.Path(_, os.pwd)),
semanticDbSourceRoot = semanticDbOptions.semanticDbSourceRoot.map(os.Path(_, os.pwd))
),
suppressExperimentalFeatureWarning = getOptionOrFromConfig(
suppress.global.suppressExperimentalFeatureWarning,
Keys.suppressExperimentalFeatureWarning
)
scalacOptions = scalacOptions
.withScalacExtraOptions(scalacExtra)
.toScalacOptShadowingSeq
.filterNonRedirected
.filterNonDeprecated
.map(Positioned.commandLine),
compilerPlugins =
SharedOptions.parseDependencies(
dependencies.compilerPlugin.map(Positioned.none),
ignoreErrors
),
platform = platformOpt.map(o => Positioned(List(Position.CommandLine()), o))
),
scalaOptions = bo.ScalaOptions(
scalaVersion = scalaVersion
.map(_.trim)
.filter(_.nonEmpty)
.map(bo.MaybeScalaVersion(_)),
scalaBinaryVersion = scalaBinaryVersion.map(_.trim).filter(_.nonEmpty),
addScalaLibrary = scalaLibrary.orElse(java.map(!_)),
addScalaCompiler = withCompiler,
semanticDbOptions = bo.SemanticDbOptions(
generateSemanticDbs = semanticDbOptions.semanticDb,
semanticDbTargetRoot = semanticDbOptions.semanticDbTargetRoot.map(os.Path(_, os.pwd)),
semanticDbSourceRoot = semanticDbOptions.semanticDbSourceRoot.map(os.Path(_, os.pwd))
scriptOptions = bo.ScriptOptions(
forceObjectWrapper = objectWrapper
),
scalacOptions = scalacOptions
.withScalacExtraOptions(scalacExtra)
.toScalacOptShadowingSeq
.filterNonRedirected
.filterNonDeprecated
.map(Positioned.commandLine),
compilerPlugins =
SharedOptions.parseDependencies(
dependencies.compilerPlugin.map(Positioned.none),
ignoreErrors
),
platform = platformOpt.map(o => Positioned(List(Position.CommandLine()), o))
),
scriptOptions = bo.ScriptOptions(
forceObjectWrapper = objectWrapper
),
scalaJsOptions = scalaJsOptions(js),
scalaNativeOptions = snOpts,
javaOptions = value(scala.cli.commands.util.JvmUtils.javaOptions(jvm)),
jmhOptions = bo.JmhOptions(
addJmhDependencies =
if (enableJmh) jmhVersion.orElse(Some(Constants.jmhVersion))
else None,
runJmh = if (enableJmh) Some(true) else None
),
classPathOptions = bo.ClassPathOptions(
extraClassPath = extraRegularJarsAndClasspath,
extraCompileOnlyJars = extraCompileOnlyClassPath,
extraSourceJars = extraSourceJars.extractedClassPath ++ assumedSourceJars,
extraRepositories =
(ScalaCli.launcherOptions.scalaRunner.cliPredefinedRepository ++ dependencies.repository)
.map(_.trim)
.filter(_.nonEmpty),
extraDependencies = extraDependencies(ignoreErrors, resolvedToolkitDependency),
extraCompileOnlyDependencies =
extraCompileOnlyDependencies(ignoreErrors, resolvedToolkitDependency)
),
internal = bo.InternalOptions(
cache = Some(coursierCache),
localRepository = LocalRepo.localRepo(Directories.directories.localRepoDir, logger),
verbosity = Some(logging.verbosity),
strictBloopJsonCheck = strictBloopJsonCheck,
interactive = Some(() => interactive),
exclude = exclude.map(Positioned.commandLine),
offline = coursier.getOffline()
),
notForBloopOptions = bo.PostBuildOptions(
scalaJsLinkerOptions = linkerOptions(js),
addRunnerDependencyOpt = runner,
python = sharedPython.python,
pythonSetup = sharedPython.pythonSetup,
scalaPyVersion = sharedPython.scalaPyVersion
),
useBuildServer = compilationServer.server
)
}
scalaJsOptions = scalaJsOptions(js),
scalaNativeOptions = snOpts,
javaOptions = value(scala.cli.commands.util.JvmUtils.javaOptions(jvm)),
jmhOptions = bo.JmhOptions(
jmhVersion = benchmarking.jmhVersion,
enableJmh = benchmarking.jmh,
runJmh = benchmarking.jmh
),
classPathOptions = bo.ClassPathOptions(
extraClassPath = extraRegularJarsAndClasspath,
extraCompileOnlyJars = extraCompileOnlyClassPath,
extraSourceJars = extraSourceJars.extractedClassPath ++ assumedSourceJars,
extraRepositories =
(ScalaCli.launcherOptions.scalaRunner.cliPredefinedRepository ++ dependencies.repository)
.map(_.trim)
.filter(_.nonEmpty),
extraDependencies = extraDependencies(ignoreErrors, resolvedToolkitDependency),
extraCompileOnlyDependencies =
extraCompileOnlyDependencies(ignoreErrors, resolvedToolkitDependency)
),
internal = bo.InternalOptions(
cache = Some(coursierCache),
localRepository = LocalRepo.localRepo(Directories.directories.localRepoDir),
verbosity = Some(logging.verbosity),
strictBloopJsonCheck = strictBloopJsonCheck,
interactive = Some(() => interactive),
exclude = exclude.map(Positioned.commandLine),
offline = coursier.getOffline()
),
notForBloopOptions = bo.PostBuildOptions(
scalaJsLinkerOptions = linkerOptions(js),
addRunnerDependencyOpt = runner,
python = sharedPython.python,
pythonSetup = sharedPython.pythonSetup,
scalaPyVersion = sharedPython.scalaPyVersion
),
useBuildServer = compilationServer.server
)
}

private def resolvedDependencies(
deps: List[String],
Expand All @@ -460,17 +456,7 @@ final case class SharedOptions(
private def extraCompileOnlyDependencies(
ignoreErrors: Boolean,
resolvedDeps: Seq[Positioned[AnyDependency]]
) = {
val jmhCorePrefix = s"${Constants.jmhOrg}:${Constants.jmhCoreModule}"
val jmhDeps =
if benchmarking.jmh.getOrElse(false) &&
!dependencies.compileOnlyDependency.exists(_.startsWith(jmhCorePrefix)) &&
!dependencies.dependency.exists(_.startsWith(jmhCorePrefix))
then List(s"$jmhCorePrefix:${Constants.jmhVersion}")
else List.empty
val finalDeps = dependencies.compileOnlyDependency ++ jmhDeps
resolvedDependencies(finalDeps, ignoreErrors, resolvedDeps)
}
) = resolvedDependencies(dependencies.compileOnlyDependency, ignoreErrors, resolvedDeps)

private def extraDependencies(
ignoreErrors: Boolean,
Expand Down Expand Up @@ -580,7 +566,7 @@ final case class SharedOptions(

def bloopRifleConfig(extraBuildOptions: Option[BuildOptions] = None)
: Either[BuildException, BloopRifleConfig] = either {
val options = extraBuildOptions.foldLeft(value(buildOptions(false, None)))(_ orElse _)
val options = extraBuildOptions.foldLeft(value(buildOptions()))(_ orElse _)
lazy val defaultJvmHome = value {
JvmUtils.downloadJvm(OsLibc.defaultJvm(OsLibc.jvmIndexOs), options)
}
Expand Down
Loading

0 comments on commit 974ebb8

Please sign in to comment.