@@ -8,6 +8,7 @@ import java.util.concurrent.Executor
88
99import scala .collection .mutable
1010import scala .concurrent .Promise
11+ import scala .util .control .NonFatal
1112
1213import bloop .io .AbsolutePath
1314import bloop .io .ParallelOps
@@ -23,6 +24,7 @@ import bloop.task.Task
2324import bloop .tracing .BraveTracer
2425import bloop .util .AnalysisUtils
2526import bloop .util .CacheHashCode
27+ import bloop .util .JavaRuntime
2628import bloop .util .UUIDUtil
2729
2830import monix .execution .Scheduler
@@ -278,39 +280,14 @@ object Compiler {
278280 )
279281 }
280282
281- var isFatalWarningsEnabled : Boolean = false
283+ val isFatalWarningsEnabled : Boolean =
284+ compileInputs.scalacOptions.exists(_ == " -Xfatal-warnings" )
282285 def getInputs (compilers : Compilers ): Inputs = {
283- val options = getCompilationOptions(compileInputs)
286+ val options = getCompilationOptions(compileInputs, logger, newClassesDir )
284287 val setup = getSetup(compileInputs)
285288 Inputs .of(compilers, options, setup, compileInputs.previousResult)
286289 }
287290
288- def getCompilationOptions (inputs : CompileInputs ): CompileOptions = {
289- // Sources are all files
290- val sources = inputs.sources.map(path => converter.toVirtualFile(path.underlying))
291- val classpath = inputs.classpath.map(path => converter.toVirtualFile(path.underlying))
292- val optionsWithoutFatalWarnings = inputs.scalacOptions.flatMap { option =>
293- if (option != " -Xfatal-warnings" ) List (option)
294- else {
295- if (! isFatalWarningsEnabled) isFatalWarningsEnabled = true
296- Nil
297- }
298- }
299-
300- // Enable fatal warnings in the reporter if they are enabled in the build
301- if (isFatalWarningsEnabled)
302- inputs.reporter.enableFatalWarnings()
303-
304- CompileOptions
305- .create()
306- .withClassesDirectory(newClassesDir)
307- .withSources(sources)
308- .withClasspath(classpath)
309- .withScalacOptions(optionsWithoutFatalWarnings)
310- .withJavacOptions(inputs.javacOptions)
311- .withOrder(inputs.compileOrder)
312- }
313-
314291 def getSetup (compileInputs : CompileInputs ): Setup = {
315292 val skip = false
316293 val empty = Array .empty[T2 [String , String ]]
@@ -627,6 +604,85 @@ object Compiler {
627604 }
628605 }
629606
607+ /**
608+ * Bloop runs Scala compilation in the same process as the main server,
609+ * so the compilation process will use the same JDK that Bloop is using.
610+ * That's why we must ensure that produce class files will be compliant with expected JDK version
611+ * and compilation errors will show up when using wrong JDK API.
612+ */
613+ private def adjustScalacReleaseOptions (
614+ scalacOptions : Array [String ],
615+ javacBin : Option [AbsolutePath ],
616+ logger : Logger
617+ ): Array [String ] = {
618+ def existsReleaseSetting = scalacOptions.exists(opt =>
619+ opt.startsWith(" -release" ) ||
620+ opt.startsWith(" --release" ) ||
621+ opt.startsWith(" -java-output-version" )
622+ )
623+ def sameHome = javacBin match {
624+ case Some (bin) => bin.getParent.getParent == JavaRuntime .home
625+ case None => false
626+ }
627+
628+ javacBin.flatMap(binary =>
629+ // <JAVA_HOME>/bin/java
630+ JavaRuntime .getJavaVersionFromJavaHome(binary.getParent.getParent)
631+ ) match {
632+ case None => scalacOptions
633+ case Some (_) if existsReleaseSetting || sameHome => scalacOptions
634+ case Some (version) =>
635+ try {
636+ val numVer = if (version.startsWith(" 1.8" )) 8 else version.takeWhile(_.isDigit).toInt
637+ val bloopNumVer = JavaRuntime .version.takeWhile(_.isDigit).toInt
638+ if (bloopNumVer > numVer) {
639+ scalacOptions ++ List (" -release" , numVer.toString())
640+ } else {
641+ logger.warn(
642+ s " Bloop is runing with ${JavaRuntime .version} but your code requires $version to compile, " +
643+ " this might cause some compilation issues when using JDK API unsupported by the Bloop's current JVM version"
644+ )
645+ scalacOptions
646+ }
647+ } catch {
648+ case NonFatal (_) =>
649+ scalacOptions
650+ }
651+ }
652+ }
653+
654+ private def getCompilationOptions (
655+ inputs : CompileInputs ,
656+ logger : Logger ,
657+ newClassesDir : Path
658+ ): CompileOptions = {
659+ // Sources are all files
660+ val sources = inputs.sources.map(path => converter.toVirtualFile(path.underlying))
661+ val classpath = inputs.classpath.map(path => converter.toVirtualFile(path.underlying))
662+
663+ val scalacOptions = adjustScalacReleaseOptions(
664+ scalacOptions = inputs.scalacOptions,
665+ javacBin = inputs.javacBin,
666+ logger = logger
667+ )
668+
669+ val optionsWithoutFatalWarnings = scalacOptions.filter(_ != " -Xfatal-warnings" )
670+ val areFatalWarningsEnabled = scalacOptions.length != optionsWithoutFatalWarnings.length
671+
672+ // Enable fatal warnings in the reporter if they are enabled in the build
673+ if (areFatalWarningsEnabled)
674+ inputs.reporter.enableFatalWarnings()
675+
676+ CompileOptions
677+ .create()
678+ .withClassesDirectory(newClassesDir)
679+ .withSources(sources)
680+ .withClasspath(classpath)
681+ .withScalacOptions(optionsWithoutFatalWarnings)
682+ .withJavacOptions(inputs.javacOptions)
683+ .withOrder(inputs.compileOrder)
684+ }
685+
630686 def toBackgroundTasks (
631687 tasks : List [(AbsolutePath , Reporter , BraveTracer ) => Task [Unit ]]
632688 ): CompileBackgroundTasks = {
0 commit comments