diff --git a/modules/build/src/main/scala/scala/build/internal/util/WarningMessages.scala b/modules/build/src/main/scala/scala/build/internal/util/WarningMessages.scala index 14d4385183..611f4b619d 100644 --- a/modules/build/src/main/scala/scala/build/internal/util/WarningMessages.scala +++ b/modules/build/src/main/scala/scala/build/internal/util/WarningMessages.scala @@ -101,4 +101,7 @@ object WarningMessages { val offlineModeBloopNotFound = "Offline mode is ON and Bloop could not be fetched from the local cache, using scalac as fallback" + + val offlineModeBloopJvmNotFound = + "Offline mode is ON and a JVM for Bloop could not be fetched from the local cache, using scalac as fallback" } diff --git a/modules/cli/src/main/scala/scala/cli/commands/bloop/Bloop.scala b/modules/cli/src/main/scala/scala/cli/commands/bloop/Bloop.scala index 14e1bbeabc..5dc70f01ec 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/bloop/Bloop.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/bloop/Bloop.scala @@ -39,7 +39,7 @@ object Bloop extends ScalaCommand[BloopOptions] { .map(JvmUtils.downloadJvm(_, options)) .getOrElse { JvmUtils.getJavaCmdVersionOrHigher(17, options) - } + }.orExit(logger) opts.compilationServer.bloopRifleConfig( opts.global.logging.logger, diff --git a/modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala b/modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala index aefba17483..ec840cb687 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/pgp/PgpExternalCommand.scala @@ -159,7 +159,7 @@ object PgpExternalCommand { JvmUtils.getJavaCmdVersionOrHigher( scalaCliSigningJvmVersion, buildOptions - ) + ).orThrow launcher( cache, diff --git a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala index f0a6453472..f9abff76d8 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/shared/SharedOptions.scala @@ -18,11 +18,12 @@ import scala.build.Ops.EitherOptOps import scala.build.* import scala.build.compiler.{BloopCompilerMaker, ScalaCompilerMaker, SimpleScalaCompilerMaker} import scala.build.directives.DirectiveDescription -import scala.build.errors.{AmbiguousPlatformError, BuildException, ConfigDbException} +import scala.build.errors.{AmbiguousPlatformError, BuildException, ConfigDbException, Severity} import scala.build.input.{Element, Inputs, ResourceDirectory, ScalaCliInvokeData} import scala.build.interactive.Interactive import scala.build.interactive.Interactive.{InteractiveAsk, InteractiveNop} import scala.build.internal.util.ConsoleUtils.ScalaCliConsole +import scala.build.internal.util.WarningMessages import scala.build.internal.{Constants, FetchExternalBinary, ObjectCodeWrapper, OsLibc, Util} import scala.build.options.ScalaVersionUtil.fileWithTtl0 import scala.build.options.{ComputeVersion, Platform, ScalacOpt, ShadowingSeq} @@ -514,15 +515,19 @@ final case class SharedOptions( def bloopRifleConfig(): Either[BuildException, BloopRifleConfig] = either { val options = value(buildOptions(false, None)) - lazy val defaultJvmCmd = - JvmUtils.downloadJvm(OsLibc.baseDefaultJvm(OsLibc.jvmIndexOs, "17"), options) - val javaCmd = compilationServer.bloopJvm.map(JvmUtils.downloadJvm(_, options)).orElse { - for (javaHome <- options.javaHomeLocationOpt()) yield { - val (javaHomeVersion, javaHomeCmd) = OsLibc.javaHomeVersion(javaHome.value) - if (javaHomeVersion >= 17) javaHomeCmd - else defaultJvmCmd - } - }.getOrElse(defaultJvmCmd) + lazy val defaultJvmCmd = value { + JvmUtils.downloadJvm(OsLibc.defaultJvm(OsLibc.jvmIndexOs), options) + } + + val javaCmd = compilationServer.bloopJvm + .map(jvmId => value(JvmUtils.downloadJvm(jvmId, options))) + .orElse { + for (javaHome <- options.javaHomeLocationOpt()) yield { + val (javaHomeVersion, javaHomeCmd) = OsLibc.javaHomeVersion(javaHome.value) + if (javaHomeVersion >= 17) javaHomeCmd + else defaultJvmCmd + } + }.getOrElse(defaultJvmCmd) compilationServer.bloopRifleConfig( logging.logger, @@ -537,19 +542,25 @@ final case class SharedOptions( def compilerMaker( threads: BuildThreads, scaladoc: Boolean = false - ): Either[BuildException, ScalaCompilerMaker] = either { + ): Either[BuildException, ScalaCompilerMaker] = if (scaladoc) - SimpleScalaCompilerMaker("java", Nil, scaladoc = true) + Right(SimpleScalaCompilerMaker("java", Nil, scaladoc = true)) else if (compilationServer.server.getOrElse(true)) - new BloopCompilerMaker( - value(bloopRifleConfig()), - threads.bloop, - strictBloopJsonCheckOrDefault, - coursier.offline.getOrElse(false) - ) + bloopRifleConfig() match { + case Right(config) => + Right(new BloopCompilerMaker( + config, + threads.bloop, + strictBloopJsonCheckOrDefault, + coursier.offline.getOrElse(false) + )) + case Left(ex) if coursier.offline.contains(true) => + logger.diagnostic(WarningMessages.offlineModeBloopJvmNotFound, Severity.Warning) + Right(SimpleScalaCompilerMaker("java", Nil)) + case Left(ex) => Left(ex) + } else - SimpleScalaCompilerMaker("java", Nil) - } + Right(SimpleScalaCompilerMaker("java", Nil)) lazy val coursierCache = coursier.coursierCache(logging.logger.coursierLogger("")) diff --git a/modules/cli/src/main/scala/scala/cli/commands/util/JvmUtils.scala b/modules/cli/src/main/scala/scala/cli/commands/util/JvmUtils.scala index ad5e64ee37..8ee5898e71 100644 --- a/modules/cli/src/main/scala/scala/cli/commands/util/JvmUtils.scala +++ b/modules/cli/src/main/scala/scala/cli/commands/util/JvmUtils.scala @@ -4,15 +4,15 @@ package util import java.io.File import scala.build.EitherCps.{either, value} -import scala.build.errors.{BuildException, UnrecognizedDebugModeError} +import scala.build.errors.{BuildException, JvmDownloadError, UnrecognizedDebugModeError} import scala.build.internal.CsLoggerUtil.* import scala.build.internal.OsLibc import scala.build.options.{JavaOpt, JavaOptions, ShadowingSeq} import scala.build.{Os, Position, Positioned, options as bo} import scala.cli.commands.shared.{CoursierOptions, SharedJvmOptions, SharedOptions} import scala.concurrent.ExecutionContextExecutorService -import scala.util.Properties import scala.util.control.NonFatal +import scala.util.{Failure, Properties, Success, Try} object JvmUtils { def javaOptions(opts: SharedJvmOptions): Either[BuildException, JavaOptions] = either { @@ -67,38 +67,43 @@ object JvmUtils { ) } - def downloadJvm(jvmId: String, options: bo.BuildOptions): String = { + def downloadJvm(jvmId: String, options: bo.BuildOptions): Either[BuildException, String] = { implicit val ec: ExecutionContextExecutorService = options.finalCache.ec val javaHomeManager = options.javaHomeManager .withMessage(s"Downloading JVM $jvmId") val logger = javaHomeManager.cache .flatMap(_.archiveCache.cache.loggerOpt) .getOrElse(_root_.coursier.cache.CacheLogger.nop) - val command = { - val path = logger.use { - try javaHomeManager.get(jvmId).unsafeRun() - catch { - case NonFatal(e) => throw new Exception(e) - } - } - os.Path(path) + + val javaHomePathOrError = Try(javaHomeManager.get(jvmId).unsafeRun()) match { + case Success(path) => Right(path) + case Failure(e) => Left(JvmDownloadError(jvmId, e)) + } + + for { + javaHomePath <- javaHomePathOrError + } yield { + val javaHome = os.Path(javaHomePath) + val ext = if (Properties.isWin) ".exe" else "" + val javaCmd = (javaHome / "bin" / s"java$ext").toString + javaCmd } - val ext = if (Properties.isWin) ".exe" else "" - val javaCmd = (command / "bin" / s"java$ext").toString - javaCmd } def getJavaCmdVersionOrHigher( javaVersion: Int, options: bo.BuildOptions - ): String = { + ): Either[BuildException, String] = { val javaHomeCmdOpt = for { javaHome <- options.javaHomeLocationOpt() (javaHomeVersion, javaHomeCmd) = OsLibc.javaHomeVersion(javaHome.value) if javaHomeVersion >= javaVersion } yield javaHomeCmd - javaHomeCmdOpt.getOrElse(downloadJvm(javaVersion.toString, options)) + javaHomeCmdOpt match { + case Some(cmd) => Right(cmd) + case None => downloadJvm(javaVersion.toString, options) + } } def getJavaCmdVersionOrHigher( @@ -110,6 +115,7 @@ object JvmUtils { for { options <- sharedOpts.buildOptions() - } yield getJavaCmdVersionOrHigher(javaVersion, options) + javaCmd <- getJavaCmdVersionOrHigher(javaVersion, options) + } yield javaCmd } } diff --git a/modules/core/src/main/scala/scala/build/errors/JvmDownloadError.scala b/modules/core/src/main/scala/scala/build/errors/JvmDownloadError.scala new file mode 100644 index 0000000000..07b24df311 --- /dev/null +++ b/modules/core/src/main/scala/scala/build/errors/JvmDownloadError.scala @@ -0,0 +1,9 @@ +package scala.build.errors + +import scala.build.Position + +final class JvmDownloadError(jvmId: String, cause: Throwable) + extends BuildException( + s"Cannot download JVM: $jvmId", + cause = cause + )