All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.build.Bloop.scala Maven / Gradle / Ivy

package scala.build

import bloop.rifle.{BloopRifleConfig, BuildServer}
import ch.epfl.scala.bsp4j
import coursier.cache.FileCache
import coursier.util.Task
import dependency.parser.ModuleParser
import dependency.{AnyDependency, DependencyLike, ScalaParameters, ScalaVersion}

import java.io.{File, IOException}

import scala.annotation.tailrec
import scala.build.EitherCps.{either, value}
import scala.build.errors.{BuildException, ModuleFormatError}
import scala.build.internal.CsLoggerUtil._
import scala.concurrent.duration.FiniteDuration
import scala.jdk.CollectionConverters._

object Bloop {

  private object BrokenPipeInCauses {
    @tailrec
    def unapply(ex: Throwable): Option[IOException] =
      ex match {
        case null                                                           => None
        case ex: IOException if ex.getMessage == "Broken pipe"              => Some(ex)
        case ex: IOException if ex.getMessage == "Connection reset by peer" => Some(ex)
        case _                                                              => unapply(ex.getCause)
      }
  }

  def compile(
    projectName: String,
    buildServer: BuildServer,
    logger: Logger,
    buildTargetsTimeout: FiniteDuration
  ): Either[Throwable, Boolean] =
    try {
      logger.debug("Listing BSP build targets")
      val results = buildServer.workspaceBuildTargets()
        .get(buildTargetsTimeout.length, buildTargetsTimeout.unit)
      val buildTargetOpt = results.getTargets.asScala.find(_.getDisplayName == projectName)

      val buildTarget = buildTargetOpt.getOrElse {
        throw new Exception(
          s"Expected to find project '$projectName' in build targets (only got ${results.getTargets.asScala.map("'" + _.getDisplayName + "'").mkString(", ")})"
        )
      }

      logger.debug(s"Compiling $projectName with Bloop")
      val compileRes = buildServer.buildTargetCompile(
        new bsp4j.CompileParams(List(buildTarget.getId).asJava)
      ).get()

      val success = compileRes.getStatusCode == bsp4j.StatusCode.OK
      logger.debug(if (success) "Compilation succeeded" else "Compilation failed")
      Right(success)
    }
    catch {
      case ex @ BrokenPipeInCauses(e) =>
        logger.debug(s"Caught $ex while exchanging with Bloop server, assuming Bloop server exited")
        Left(ex)
    }

  def bloopClassPath(
    dep: AnyDependency,
    params: ScalaParameters,
    logger: Logger,
    cache: FileCache[Task]
  ): Either[BuildException, Seq[File]] =
    either {
      val res = value {
        Artifacts.artifacts(
          Seq(Positioned.none(dep)),
          Nil,
          Some(params),
          logger,
          cache.withMessage(s"Downloading compilation server ${dep.version}")
        )
      }
      res.map(_._2.toIO)
    }

  def bloopClassPath(
    logger: Logger,
    cache: FileCache[Task]
  ): Either[BuildException, (Seq[File], Boolean)] =
    bloopClassPath(logger, cache, BloopRifleConfig.defaultVersion)

  def bloopClassPath(
    logger: Logger,
    cache: FileCache[Task],
    bloopVersion: String
  ): Either[BuildException, (Seq[File], Boolean)] = either {
    val moduleStr = BloopRifleConfig.defaultModule
    val mod = value {
      ModuleParser.parse(moduleStr)
        .left.map(err => new ModuleFormatError(moduleStr, err, Some("Bloop")))
    }
    val dep             = DependencyLike(mod, bloopVersion)
    val sv              = BloopRifleConfig.defaultScalaVersion
    val sbv             = ScalaVersion.binary(sv)
    val params          = ScalaParameters(sv, sbv)
    val cp              = value(bloopClassPath(dep, params, logger, cache))
    val isScalaCliBloop = moduleStr.startsWith(BloopRifleConfig.scalaCliBloopOrg + ":")
    (cp, isScalaCliBloop)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy