Skip to content

Commit c2ce8cd

Browse files
committed
Add using directives for benchmarking with JMH
1 parent ccd1ceb commit c2ce8cd

File tree

14 files changed

+388
-258
lines changed

14 files changed

+388
-258
lines changed

build.sc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,11 @@ trait CliIntegration extends SbtModule with ScalaCliPublishModule with HasTests
10451045
|
10461046
| def ghOrg = "$ghOrg"
10471047
| def ghName = "$ghName"
1048+
|
1049+
| def jmhVersion = "${Deps.Versions.jmh}"
1050+
| def jmhOrg = "${Deps.jmhCore.dep.module.organization.value}"
1051+
| def jmhCoreModule = "${Deps.jmhCore.dep.module.name.value}"
1052+
| def jmhGeneratorBytecodeModule = "${Deps.jmhGeneratorBytecode.dep.module.name.value}"
10481053
|}
10491054
|""".stripMargin
10501055
if (!os.isFile(dest) || os.read(dest) != code)

modules/build/src/main/scala/scala/build/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ object Build {
459459

460460
build0 match {
461461
case successful: Successful =>
462-
if (options.jmhOptions.runJmh.getOrElse(false) && scope == Scope.Main)
462+
if (options.jmhOptions.canRunJmh && scope == Scope.Main)
463463
value {
464464
val res = jmhBuild(
465465
inputs,

modules/build/src/main/scala/scala/build/preprocessing/directives/DirectivesPreprocessingUtils.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import scala.build.preprocessing.directives
1212
object DirectivesPreprocessingUtils {
1313
val usingDirectiveHandlers: Seq[DirectiveHandler[BuildOptions]] =
1414
Seq[DirectiveHandler[_ <: HasBuildOptions]](
15+
directives.Benchmarking.handler,
1516
directives.BuildInfo.handler,
1617
directives.ComputeVersion.handler,
1718
directives.Exclude.handler,

modules/cli/src/main/scala/scala/cli/commands/bloop/Bloop.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ object Bloop extends ScalaCommand[BloopOptions] {
3333
jvm = opts.jvm,
3434
coursier = opts.coursier
3535
)
36-
val options = sharedOptions.buildOptions(false, None).orExit(opts.global.logging.logger)
36+
val options = sharedOptions.buildOptions().orExit(opts.global.logging.logger)
3737

3838
val javaHomeInfo = opts.compilationServer.bloopJvm
3939
.map(JvmUtils.downloadJvm(_, options))

modules/cli/src/main/scala/scala/cli/commands/doc/Doc.scala

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,7 @@ object Doc extends ScalaCommand[DocOptions] {
3030

3131
override def buildOptions(options: DocOptions): Option[BuildOptions] =
3232
sharedOptions(options)
33-
.map(shared =>
34-
shared.buildOptions(
35-
enableJmh = shared.benchmarking.jmh.getOrElse(false),
36-
jmhVersion = shared.benchmarking.jmhVersion
37-
).orExit(shared.logger)
38-
)
33+
.map(shared => shared.buildOptions().orExit(shared.logger))
3934

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

modules/cli/src/main/scala/scala/cli/commands/run/Run.scala

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
7272
override def buildOptions(options: RunOptions): Some[BuildOptions] = Some {
7373
import options.*
7474
import options.sharedRun.*
75-
val logger = options.shared.logger
76-
val baseOptions = shared.buildOptions(
77-
enableJmh = shared.benchmarking.jmh.contains(true),
78-
jmhVersion = shared.benchmarking.jmhVersion
79-
).orExit(logger)
75+
val logger = options.shared.logger
76+
val baseOptions = shared.buildOptions().orExit(logger)
8077
baseOptions.copy(
8178
mainClass = mainClass.mainClass,
8279
javaOptions = baseOptions.javaOptions.copy(
@@ -375,7 +372,8 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
375372

376373
val mainClassOpt = build.options.mainClass.filter(_.nonEmpty) // trim it too?
377374
.orElse {
378-
if (build.options.jmhOptions.runJmh.contains(false)) Some("org.openjdk.jmh.Main")
375+
if build.options.jmhOptions.enableJmh.contains(true) && !build.options.jmhOptions.canRunJmh
376+
then Some("org.openjdk.jmh.Main")
379377
else None
380378
}
381379
val mainClass = mainClassOpt match {

modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala

Lines changed: 138 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -301,152 +301,148 @@ final case class SharedOptions(
301301

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

304-
def buildOptions(
305-
enableJmh: Boolean = false,
306-
jmhVersion: Option[String] = None,
307-
ignoreErrors: Boolean = false
308-
): Either[BuildException, bo.BuildOptions] = either {
309-
val releaseOpt = scalacOptions.getScalacOption("-release")
310-
val targetOpt = scalacOptions.getScalacPrefixOption("-target")
311-
jvm.jvm -> (releaseOpt.toSeq ++ targetOpt) match {
312-
case (Some(j), compilerTargets) if compilerTargets.exists(_ != j) =>
313-
val compilerTargetsString = compilerTargets.distinct.mkString(", ")
314-
logger.error(
315-
s"Warning: different target JVM ($j) and scala compiler target JVM ($compilerTargetsString) were passed."
316-
)
317-
case _ =>
318-
}
319-
val parsedPlatform = platform.map(Platform.normalize).flatMap(Platform.parse)
320-
val platformOpt = value {
321-
(parsedPlatform, js.js, native.native) match {
322-
case (Some(p: Platform.JS.type), _, false) => Right(Some(p))
323-
case (Some(p: Platform.Native.type), false, _) => Right(Some(p))
324-
case (Some(p: Platform.JVM.type), false, false) => Right(Some(p))
325-
case (Some(p), _, _) =>
326-
val jsSeq = if (js.js) Seq(Platform.JS) else Seq.empty
327-
val nativeSeq = if (native.native) Seq(Platform.Native) else Seq.empty
328-
val platformsSeq = Seq(p) ++ jsSeq ++ nativeSeq
329-
Left(new AmbiguousPlatformError(platformsSeq.distinct.map(_.toString)))
330-
case (_, true, true) =>
331-
Left(new AmbiguousPlatformError(Seq(Platform.JS.toString, Platform.Native.toString)))
332-
case (_, true, _) => Right(Some(Platform.JS))
333-
case (_, _, true) => Right(Some(Platform.Native))
334-
case _ => Right(None)
304+
def buildOptions(ignoreErrors: Boolean = false): Either[BuildException, bo.BuildOptions] =
305+
either {
306+
val releaseOpt = scalacOptions.getScalacOption("-release")
307+
val targetOpt = scalacOptions.getScalacPrefixOption("-target")
308+
jvm.jvm -> (releaseOpt.toSeq ++ targetOpt) match {
309+
case (Some(j), compilerTargets) if compilerTargets.exists(_ != j) =>
310+
val compilerTargetsString = compilerTargets.distinct.mkString(", ")
311+
logger.error(
312+
s"Warning: different target JVM ($j) and scala compiler target JVM ($compilerTargetsString) were passed."
313+
)
314+
case _ =>
335315
}
336-
}
337-
val (assumedSourceJars, extraRegularJarsAndClasspath) =
338-
extraJarsAndClassPath.partition(_.hasSourceJarSuffix)
339-
if assumedSourceJars.nonEmpty then
340-
val assumedSourceJarsString = assumedSourceJars.mkString(", ")
341-
logger.message(
342-
s"""[${Console.YELLOW}warn${Console.RESET}] Jars with the ${ScalaCliConsole
343-
.GRAY}*-sources.jar${Console.RESET} name suffix are assumed to be source jars.
344-
|The following jars were assumed to be source jars and will be treated as such: $assumedSourceJarsString""".stripMargin
345-
)
346-
val (resolvedToolkitDependency, toolkitMaxDefaultScalaNativeVersions) =
347-
SharedOptions.resolveToolkitDependencyAndScalaNativeVersionReqs(withToolkit, logger)
348-
val scalapyMaxDefaultScalaNativeVersions =
349-
if sharedPython.python.contains(true) then
350-
List(Constants.scalaPyMaxScalaNative -> Python.maxScalaNativeWarningMsg)
351-
else Nil
352-
val maxDefaultScalaNativeVersions =
353-
toolkitMaxDefaultScalaNativeVersions.toList ++ scalapyMaxDefaultScalaNativeVersions
354-
val snOpts = scalaNativeOptions(native, maxDefaultScalaNativeVersions)
355-
bo.BuildOptions(
356-
sourceGeneratorOptions = bo.SourceGeneratorOptions(
357-
useBuildInfo = sourceGenerator.useBuildInfo,
358-
projectVersion = sharedVersionOptions.projectVersion,
359-
computeVersion = value {
360-
sharedVersionOptions.computeVersion
361-
.map(Positioned.commandLine)
362-
.map(ComputeVersion.parse)
363-
.sequence
316+
val parsedPlatform = platform.map(Platform.normalize).flatMap(Platform.parse)
317+
val platformOpt = value {
318+
(parsedPlatform, js.js, native.native) match {
319+
case (Some(p: Platform.JS.type), _, false) => Right(Some(p))
320+
case (Some(p: Platform.Native.type), false, _) => Right(Some(p))
321+
case (Some(p: Platform.JVM.type), false, false) => Right(Some(p))
322+
case (Some(p), _, _) =>
323+
val jsSeq = if (js.js) Seq(Platform.JS) else Seq.empty
324+
val nativeSeq = if (native.native) Seq(Platform.Native) else Seq.empty
325+
val platformsSeq = Seq(p) ++ jsSeq ++ nativeSeq
326+
Left(new AmbiguousPlatformError(platformsSeq.distinct.map(_.toString)))
327+
case (_, true, true) =>
328+
Left(new AmbiguousPlatformError(Seq(Platform.JS.toString, Platform.Native.toString)))
329+
case (_, true, _) => Right(Some(Platform.JS))
330+
case (_, _, true) => Right(Some(Platform.Native))
331+
case _ => Right(None)
364332
}
365-
),
366-
suppressWarningOptions =
367-
bo.SuppressWarningOptions(
368-
suppressDirectivesInMultipleFilesWarning = getOptionOrFromConfig(
369-
suppress.suppressDirectivesInMultipleFilesWarning,
370-
Keys.suppressDirectivesInMultipleFilesWarning
333+
}
334+
val (assumedSourceJars, extraRegularJarsAndClasspath) =
335+
extraJarsAndClassPath.partition(_.hasSourceJarSuffix)
336+
if assumedSourceJars.nonEmpty then
337+
val assumedSourceJarsString = assumedSourceJars.mkString(", ")
338+
logger.message(
339+
s"""[${Console.YELLOW}warn${Console.RESET}] Jars with the ${ScalaCliConsole
340+
.GRAY}*-sources.jar${Console.RESET} name suffix are assumed to be source jars.
341+
|The following jars were assumed to be source jars and will be treated as such: $assumedSourceJarsString""".stripMargin
342+
)
343+
val (resolvedToolkitDependency, toolkitMaxDefaultScalaNativeVersions) =
344+
SharedOptions.resolveToolkitDependencyAndScalaNativeVersionReqs(withToolkit, logger)
345+
val scalapyMaxDefaultScalaNativeVersions =
346+
if sharedPython.python.contains(true) then
347+
List(Constants.scalaPyMaxScalaNative -> Python.maxScalaNativeWarningMsg)
348+
else Nil
349+
val maxDefaultScalaNativeVersions =
350+
toolkitMaxDefaultScalaNativeVersions.toList ++ scalapyMaxDefaultScalaNativeVersions
351+
val snOpts = scalaNativeOptions(native, maxDefaultScalaNativeVersions)
352+
bo.BuildOptions(
353+
sourceGeneratorOptions = bo.SourceGeneratorOptions(
354+
useBuildInfo = sourceGenerator.useBuildInfo,
355+
projectVersion = sharedVersionOptions.projectVersion,
356+
computeVersion = value {
357+
sharedVersionOptions.computeVersion
358+
.map(Positioned.commandLine)
359+
.map(ComputeVersion.parse)
360+
.sequence
361+
}
362+
),
363+
suppressWarningOptions =
364+
bo.SuppressWarningOptions(
365+
suppressDirectivesInMultipleFilesWarning = getOptionOrFromConfig(
366+
suppress.suppressDirectivesInMultipleFilesWarning,
367+
Keys.suppressDirectivesInMultipleFilesWarning
368+
),
369+
suppressOutdatedDependencyWarning = getOptionOrFromConfig(
370+
suppress.suppressOutdatedDependencyWarning,
371+
Keys.suppressOutdatedDependenciessWarning
372+
),
373+
suppressExperimentalFeatureWarning = getOptionOrFromConfig(
374+
suppress.global.suppressExperimentalFeatureWarning,
375+
Keys.suppressExperimentalFeatureWarning
376+
)
371377
),
372-
suppressOutdatedDependencyWarning = getOptionOrFromConfig(
373-
suppress.suppressOutdatedDependencyWarning,
374-
Keys.suppressOutdatedDependenciessWarning
378+
scalaOptions = bo.ScalaOptions(
379+
scalaVersion = scalaVersion
380+
.map(_.trim)
381+
.filter(_.nonEmpty)
382+
.map(bo.MaybeScalaVersion(_)),
383+
scalaBinaryVersion = scalaBinaryVersion.map(_.trim).filter(_.nonEmpty),
384+
addScalaLibrary = scalaLibrary.orElse(java.map(!_)),
385+
addScalaCompiler = withCompiler,
386+
semanticDbOptions = bo.SemanticDbOptions(
387+
generateSemanticDbs = semanticDbOptions.semanticDb,
388+
semanticDbTargetRoot = semanticDbOptions.semanticDbTargetRoot.map(os.Path(_, os.pwd)),
389+
semanticDbSourceRoot = semanticDbOptions.semanticDbSourceRoot.map(os.Path(_, os.pwd))
375390
),
376-
suppressExperimentalFeatureWarning = getOptionOrFromConfig(
377-
suppress.global.suppressExperimentalFeatureWarning,
378-
Keys.suppressExperimentalFeatureWarning
379-
)
391+
scalacOptions = scalacOptions
392+
.withScalacExtraOptions(scalacExtra)
393+
.toScalacOptShadowingSeq
394+
.filterNonRedirected
395+
.filterNonDeprecated
396+
.map(Positioned.commandLine),
397+
compilerPlugins =
398+
SharedOptions.parseDependencies(
399+
dependencies.compilerPlugin.map(Positioned.none),
400+
ignoreErrors
401+
),
402+
platform = platformOpt.map(o => Positioned(List(Position.CommandLine()), o))
380403
),
381-
scalaOptions = bo.ScalaOptions(
382-
scalaVersion = scalaVersion
383-
.map(_.trim)
384-
.filter(_.nonEmpty)
385-
.map(bo.MaybeScalaVersion(_)),
386-
scalaBinaryVersion = scalaBinaryVersion.map(_.trim).filter(_.nonEmpty),
387-
addScalaLibrary = scalaLibrary.orElse(java.map(!_)),
388-
addScalaCompiler = withCompiler,
389-
semanticDbOptions = bo.SemanticDbOptions(
390-
generateSemanticDbs = semanticDbOptions.semanticDb,
391-
semanticDbTargetRoot = semanticDbOptions.semanticDbTargetRoot.map(os.Path(_, os.pwd)),
392-
semanticDbSourceRoot = semanticDbOptions.semanticDbSourceRoot.map(os.Path(_, os.pwd))
404+
scriptOptions = bo.ScriptOptions(
405+
forceObjectWrapper = objectWrapper
393406
),
394-
scalacOptions = scalacOptions
395-
.withScalacExtraOptions(scalacExtra)
396-
.toScalacOptShadowingSeq
397-
.filterNonRedirected
398-
.filterNonDeprecated
399-
.map(Positioned.commandLine),
400-
compilerPlugins =
401-
SharedOptions.parseDependencies(
402-
dependencies.compilerPlugin.map(Positioned.none),
403-
ignoreErrors
404-
),
405-
platform = platformOpt.map(o => Positioned(List(Position.CommandLine()), o))
406-
),
407-
scriptOptions = bo.ScriptOptions(
408-
forceObjectWrapper = objectWrapper
409-
),
410-
scalaJsOptions = scalaJsOptions(js),
411-
scalaNativeOptions = snOpts,
412-
javaOptions = value(scala.cli.commands.util.JvmUtils.javaOptions(jvm)),
413-
jmhOptions = bo.JmhOptions(
414-
addJmhDependencies =
415-
if (enableJmh) jmhVersion.orElse(Some(Constants.jmhVersion))
416-
else None,
417-
runJmh = if (enableJmh) Some(true) else None
418-
),
419-
classPathOptions = bo.ClassPathOptions(
420-
extraClassPath = extraRegularJarsAndClasspath,
421-
extraCompileOnlyJars = extraCompileOnlyClassPath,
422-
extraSourceJars = extraSourceJars.extractedClassPath ++ assumedSourceJars,
423-
extraRepositories =
424-
(ScalaCli.launcherOptions.scalaRunner.cliPredefinedRepository ++ dependencies.repository)
425-
.map(_.trim)
426-
.filter(_.nonEmpty),
427-
extraDependencies = extraDependencies(ignoreErrors, resolvedToolkitDependency),
428-
extraCompileOnlyDependencies =
429-
extraCompileOnlyDependencies(ignoreErrors, resolvedToolkitDependency)
430-
),
431-
internal = bo.InternalOptions(
432-
cache = Some(coursierCache),
433-
localRepository = LocalRepo.localRepo(Directories.directories.localRepoDir, logger),
434-
verbosity = Some(logging.verbosity),
435-
strictBloopJsonCheck = strictBloopJsonCheck,
436-
interactive = Some(() => interactive),
437-
exclude = exclude.map(Positioned.commandLine),
438-
offline = coursier.getOffline()
439-
),
440-
notForBloopOptions = bo.PostBuildOptions(
441-
scalaJsLinkerOptions = linkerOptions(js),
442-
addRunnerDependencyOpt = runner,
443-
python = sharedPython.python,
444-
pythonSetup = sharedPython.pythonSetup,
445-
scalaPyVersion = sharedPython.scalaPyVersion
446-
),
447-
useBuildServer = compilationServer.server
448-
)
449-
}
407+
scalaJsOptions = scalaJsOptions(js),
408+
scalaNativeOptions = snOpts,
409+
javaOptions = value(scala.cli.commands.util.JvmUtils.javaOptions(jvm)),
410+
jmhOptions = bo.JmhOptions(
411+
jmhVersion = benchmarking.jmhVersion,
412+
enableJmh = benchmarking.jmh,
413+
runJmh = benchmarking.jmh
414+
),
415+
classPathOptions = bo.ClassPathOptions(
416+
extraClassPath = extraRegularJarsAndClasspath,
417+
extraCompileOnlyJars = extraCompileOnlyClassPath,
418+
extraSourceJars = extraSourceJars.extractedClassPath ++ assumedSourceJars,
419+
extraRepositories =
420+
(ScalaCli.launcherOptions.scalaRunner.cliPredefinedRepository ++ dependencies.repository)
421+
.map(_.trim)
422+
.filter(_.nonEmpty),
423+
extraDependencies = extraDependencies(ignoreErrors, resolvedToolkitDependency),
424+
extraCompileOnlyDependencies =
425+
extraCompileOnlyDependencies(ignoreErrors, resolvedToolkitDependency)
426+
),
427+
internal = bo.InternalOptions(
428+
cache = Some(coursierCache),
429+
localRepository = LocalRepo.localRepo(Directories.directories.localRepoDir, logger),
430+
verbosity = Some(logging.verbosity),
431+
strictBloopJsonCheck = strictBloopJsonCheck,
432+
interactive = Some(() => interactive),
433+
exclude = exclude.map(Positioned.commandLine),
434+
offline = coursier.getOffline()
435+
),
436+
notForBloopOptions = bo.PostBuildOptions(
437+
scalaJsLinkerOptions = linkerOptions(js),
438+
addRunnerDependencyOpt = runner,
439+
python = sharedPython.python,
440+
pythonSetup = sharedPython.pythonSetup,
441+
scalaPyVersion = sharedPython.scalaPyVersion
442+
),
443+
useBuildServer = compilationServer.server
444+
)
445+
}
450446

451447
private def resolvedDependencies(
452448
deps: List[String],
@@ -460,17 +456,7 @@ final case class SharedOptions(
460456
private def extraCompileOnlyDependencies(
461457
ignoreErrors: Boolean,
462458
resolvedDeps: Seq[Positioned[AnyDependency]]
463-
) = {
464-
val jmhCorePrefix = s"${Constants.jmhOrg}:${Constants.jmhCoreModule}"
465-
val jmhDeps =
466-
if benchmarking.jmh.getOrElse(false) &&
467-
!dependencies.compileOnlyDependency.exists(_.startsWith(jmhCorePrefix)) &&
468-
!dependencies.dependency.exists(_.startsWith(jmhCorePrefix))
469-
then List(s"$jmhCorePrefix:${Constants.jmhVersion}")
470-
else List.empty
471-
val finalDeps = dependencies.compileOnlyDependency ++ jmhDeps
472-
resolvedDependencies(finalDeps, ignoreErrors, resolvedDeps)
473-
}
459+
) = resolvedDependencies(dependencies.compileOnlyDependency, ignoreErrors, resolvedDeps)
474460

475461
private def extraDependencies(
476462
ignoreErrors: Boolean,
@@ -580,7 +566,7 @@ final case class SharedOptions(
580566

581567
def bloopRifleConfig(extraBuildOptions: Option[BuildOptions] = None)
582568
: Either[BuildException, BloopRifleConfig] = either {
583-
val options = extraBuildOptions.foldLeft(value(buildOptions(false, None)))(_ orElse _)
569+
val options = extraBuildOptions.foldLeft(value(buildOptions()))(_ orElse _)
584570
lazy val defaultJvmHome = value {
585571
JvmUtils.downloadJvm(OsLibc.defaultJvm(OsLibc.jvmIndexOs), options)
586572
}

0 commit comments

Comments
 (0)