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

scala.build.Artifacts.scala Maven / Gradle / Ivy

package scala.build

import coursier.cache.FileCache
import coursier.core.{Classifier, Module, ModuleName, Organization, Repository, Version}
import coursier.error.ResolutionError
import coursier.util.Task
import coursier.{Dependency => CsDependency, Fetch, Resolution, core => csCore, util => csUtil}
import dependency.*

import java.net.URL

import scala.build.CoursierUtils.*
import scala.build.EitherCps.{either, value}
import scala.build.Ops.*
import scala.build.errors.{
  BuildException,
  CompositeBuildException,
  FetchingDependenciesError,
  NoScalaVersionProvidedError,
  ToolkitVersionError
}
import scala.build.internal.Constants
import scala.build.internal.Constants.*
import scala.build.internal.CsLoggerUtil.*
import scala.build.internal.Util.{PositionedScalaDependencyOps, ScalaModuleOps}
import scala.collection.mutable

final case class Artifacts(
  javacPluginDependencies: Seq[(AnyDependency, String, os.Path)],
  extraJavacPlugins: Seq[os.Path],
  defaultDependencies: Seq[AnyDependency],
  extraDependencies: Seq[AnyDependency],
  userCompileOnlyDependencies: Seq[AnyDependency],
  internalDependencies: Seq[AnyDependency],
  detailedArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, os.Path)],
  detailedRuntimeArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, os.Path)],
  extraClassPath: Seq[os.Path],
  extraCompileOnlyJars: Seq[os.Path],
  extraRuntimeClassPath: Seq[os.Path],
  extraSourceJars: Seq[os.Path],
  scalaOpt: Option[ScalaArtifacts],
  hasJvmRunner: Boolean,
  resolution: Option[Resolution]
) {

  def userDependencies = defaultDependencies ++ extraDependencies
  lazy val jarsForUserExtraDependencies = {
    val extraDependenciesMap =
      extraDependencies.map(dep => dep.module.name -> dep.version).toMap
    detailedArtifacts
      .iterator
      .collect {
        case (dep, pub, _, path)
            if pub.classifier != Classifier.sources &&
            extraDependenciesMap.get(dep.module.name.value).contains(dep.version) => path
      }
      .toVector
      .distinct
  }

  lazy val artifacts: Seq[(String, os.Path)] =
    detailedArtifacts
      .iterator
      .collect {
        case (_, pub, a, f) if pub.classifier != Classifier.sources =>
          (a.url, f)
      }
      .toVector
      .distinct
  lazy val runtimeArtifacts: Seq[(String, os.Path)] =
    detailedRuntimeArtifacts
      .iterator
      .collect {
        case (_, pub, a, f) if pub.classifier != Classifier.sources =>
          (a.url, f)
      }
      .toVector
      .distinct
  lazy val sourceArtifacts: Seq[(String, os.Path)] =
    detailedArtifacts
      .iterator
      .collect {
        case (_, pub, a, f) if pub.classifier == Classifier.sources =>
          (a.url, f)
      }
      .toVector
      .distinct
  lazy val classPath: Seq[os.Path] =
    runtimeArtifacts.map(_._2) ++ extraClassPath ++ extraRuntimeClassPath
  lazy val compileClassPath: Seq[os.Path] =
    artifacts.map(_._2) ++ extraClassPath ++ extraCompileOnlyJars
  lazy val sourcePath: Seq[os.Path] =
    sourceArtifacts.map(_._2) ++ extraSourceJars
}

object Artifacts {

  final case class ScalaArtifactsParams(
    params: ScalaParameters,
    compilerPlugins: Seq[Positioned[AnyDependency]],
    addJsTestBridge: Option[String],
    addNativeTestInterface: Option[String],
    scalaJsVersion: Option[String],
    scalaJsCliVersion: Option[String],
    scalaNativeCliVersion: Option[String],
    addScalapy: Option[String]
  )

  def apply(
    scalaArtifactsParamsOpt: Option[ScalaArtifactsParams],
    javacPluginDependencies: Seq[Positioned[AnyDependency]],
    extraJavacPlugins: Seq[os.Path],
    defaultDependencies: Seq[Positioned[AnyDependency]],
    extraDependencies: Seq[Positioned[AnyDependency]],
    compileOnlyDependencies: Seq[Positioned[AnyDependency]],
    extraClassPath: Seq[os.Path],
    extraCompileOnlyJars: Seq[os.Path],
    extraSourceJars: Seq[os.Path],
    fetchSources: Boolean,
    addJvmRunner: Option[Boolean],
    addJvmTestRunner: Boolean,
    addJmhDependencies: Option[String],
    extraRepositories: Seq[Repository],
    keepResolution: Boolean,
    includeBuildServerDeps: Boolean,
    cache: FileCache[Task],
    logger: Logger,
    maybeRecoverOnError: BuildException => Option[BuildException]
  ): Either[BuildException, Artifacts] = either {
    val dependencies = defaultDependencies ++ extraDependencies

    val jvmTestRunnerDependencies =
      if (addJvmTestRunner)
        Seq(dep"$testRunnerOrganization::$testRunnerModuleName:$testRunnerVersion")
      else
        Nil

    val jmhDependencies = addJmhDependencies.toSeq
      .map(version => dep"${Constants.jmhOrg}:${Constants.jmhGeneratorBytecodeModule}:$version")

    val maybeSnapshotRepo = {
      val hasSnapshots = jvmTestRunnerDependencies.exists(_.version.endsWith("SNAPSHOT")) ||
        scalaArtifactsParamsOpt.flatMap(_.scalaNativeCliVersion).exists(_.endsWith("SNAPSHOT"))
      if (hasSnapshots)
        Seq(coursier.Repositories.sonatype("snapshots"))
      else
        Nil
    }

    val allExtraRepositories =
      maybeSnapshotRepo ++ extraRepositories

    val scalaOpt = scalaArtifactsParamsOpt match {
      case Some(scalaArtifactsParams) =>
        val compilerDependencies =
          if (scalaArtifactsParams.params.scalaVersion.startsWith("3."))
            Seq(
              dep"org.scala-lang::scala3-compiler:${scalaArtifactsParams.params.scalaVersion}"
            )
          else
            Seq(
              dep"org.scala-lang:scala-compiler:${scalaArtifactsParams.params.scalaVersion}"
            )
        val compilerDependenciesMessage =
          s"Downloading Scala ${scalaArtifactsParams.params.scalaVersion} compiler"

        val compilerPlugins0 = value {
          scalaArtifactsParams.compilerPlugins
            .map { posDep =>
              val posDep0 =
                posDep.map(dep => dep.copy(userParams = dep.userParams + ("intransitive" -> None)))
              artifacts(
                Seq(posDep0),
                allExtraRepositories,
                Some(scalaArtifactsParams.params),
                logger,
                cache.withMessage(s"Downloading compiler plugin ${posDep.value.render}")
              ).map(_.map { case (url, path) => (posDep0.value, url, path) })
            }
            .sequence
            .left.flatMap {
              CompositeBuildException(_)
                .maybeRecoverWithDefault(Seq.empty, maybeRecoverOnError)
            }
            .map(_.flatten)
        }

        val compilerArtifacts = value {
          artifacts(
            compilerDependencies.map(Positioned.none),
            allExtraRepositories,
            Some(scalaArtifactsParams.params),
            logger,
            cache.withMessage(compilerDependenciesMessage)
          ).left.flatMap(_.maybeRecoverWithDefault(Seq.empty, maybeRecoverOnError))
        }

        val bridgeJarsOpt =
          (scalaArtifactsParams.params.scalaVersion -> includeBuildServerDeps match {
            case (sv, true) if sv.startsWith("3.") =>
              Some(Seq(
                dep"org.scala-lang:scala3-sbt-bridge:${scalaArtifactsParams.params.scalaVersion}"
              ))
            case (sv, true)
                if Version(sv) >= Version("2.13.12") ||
                sv.startsWith(Constants.defaultScala213Version) =>
              Some(Seq(
                dep"org.scala-lang:scala2-sbt-bridge:${scalaArtifactsParams.params.scalaVersion}"
              ))
            case _ => None
          })
            .map { bridgeDependencies =>
              value {
                artifacts(
                  bridgeDependencies.map(Positioned.none),
                  allExtraRepositories,
                  Some(scalaArtifactsParams.params),
                  logger,
                  cache.withMessage(
                    s"Downloading Scala ${scalaArtifactsParams.params.scalaVersion} bridge"
                  )
                ).left.flatMap(_.maybeRecoverWithDefault(Seq.empty, maybeRecoverOnError))
              }.map(_._2)
            }

        def fetchedArtifactToPath(fetched: Fetch.Result): Seq[os.Path] =
          fetched.fullDetailedArtifacts.collect { case (_, _, _, Some(f)) => os.Path(f, Os.pwd) }

        val scalaJsCliDependency =
          scalaArtifactsParams.scalaJsCliVersion.map { scalaJsCliVersion =>
            val mod = Module(
              Organization("org.virtuslab.scala-cli"),
              ModuleName(s"scalajscli_2.13"),
              Map.empty
            )
            Seq(coursier.Dependency(mod, s"$scalaJsCliVersion+"))
          }

        val fetchedScalaJsCli = scalaJsCliDependency match {
          case Some(dependency) =>
            val forcedVersions = Seq(
              cmod"org.scala-js:scalajs-linker_2.13" -> scalaJsVersion
            )
            Some {
              val (_, res) = value {
                fetchCsDependencies(
                  dependency.map(Positioned.none),
                  allExtraRepositories,
                  None,
                  forcedVersions,
                  logger,
                  cache.withMessage("Downloading Scala.js CLI"),
                  None
                )
              }
              res
            }
          case None =>
            None
        }

        val scalaJsCli = fetchedScalaJsCli.toSeq.flatMap(fetchedArtifactToPath)

        val scalaNativeCliDependency =
          scalaArtifactsParams.scalaNativeCliVersion.map { version =>
            val module = cmod"org.scala-native:scala-native-cli_2.12"
            Seq(coursier.Dependency(module, version))
          }

        val fetchedScalaNativeCli = scalaNativeCliDependency match {
          case Some(dependency) =>
            Some {
              val (_, res) = value {
                fetchCsDependencies(
                  dependency.map(Positioned.none),
                  allExtraRepositories,
                  None,
                  Nil,
                  logger,
                  cache.withMessage("Downloading Scala Native CLI"),
                  None
                )
              }
              res
            }
          case None =>
            None
        }

        val scalaNativeCli = fetchedScalaNativeCli.toSeq.flatMap(fetchedArtifactToPath)

        val jsTestBridgeDependencies =
          scalaArtifactsParams.addJsTestBridge.toSeq.map { scalaJsVersion =>
            if (scalaArtifactsParams.params.scalaVersion.startsWith("2."))
              dep"org.scala-js::scalajs-test-bridge:$scalaJsVersion"
            else
              dep"org.scala-js:scalajs-test-bridge_2.13:$scalaJsVersion"
          }
        val nativeTestInterfaceDependencies =
          scalaArtifactsParams.addNativeTestInterface.toSeq.map { scalaNativeVersion =>
            dep"org.scala-native::test-interface::$scalaNativeVersion"
          }

        val scalapyDependencies = scalaArtifactsParams.addScalapy match {
          case Some(scalaPyVersion) =>
            Seq(dep"${scalaPyOrganization(scalaPyVersion)}::scalapy-core::$scalaPyVersion")
          case None =>
            Nil
        }

        val internalDependencies =
          jsTestBridgeDependencies ++
            nativeTestInterfaceDependencies

        val scala = ScalaArtifacts(
          compilerDependencies,
          compilerArtifacts,
          compilerPlugins0,
          scalaJsCli,
          scalaNativeCli,
          internalDependencies,
          scalapyDependencies,
          scalaArtifactsParams.params,
          bridgeJarsOpt
        )
        Some(scala)

      case None =>
        None
    }

    val internalDependencies =
      jvmTestRunnerDependencies.map(Positioned.none) ++
        scalaOpt.toSeq.flatMap(_.internalDependencies).map(Positioned.none) ++
        jmhDependencies.map(Positioned.none)
    val updatedDependencies = dependencies ++
      scalaOpt.toSeq.flatMap(_.extraDependencies).map(Positioned.none) ++
      internalDependencies
    val allUpdatedDependencies = compileOnlyDependencies ++ updatedDependencies

    val updatedDependenciesMessage = {
      val b           = new mutable.StringBuilder("Downloading ")
      val depLen      = dependencies.length
      val extraDepLen = allUpdatedDependencies.length - depLen
      depLen match {
        case 1          => b.append("one dependency")
        case n if n > 1 => b.append(s"$n dependencies")
        case _          =>
      }

      if (depLen > 0 && extraDepLen > 0)
        b.append(" and ")

      extraDepLen match {
        case 1          => b.append("one internal dependency")
        case n if n > 1 => b.append(s"$n internal dependencies")
        case _          =>
      }

      b.result()
    }

    val (fetcher, fetchRes) = value {
      fetchAnyDependenciesWithResult(
        allUpdatedDependencies,
        allExtraRepositories,
        scalaArtifactsParamsOpt.map(_.params),
        logger,
        cache.withMessage(updatedDependenciesMessage),
        classifiersOpt = Some(Set("_") ++ (if (fetchSources) Set("sources") else Set.empty)),
        maybeRecoverOnError
      )
    }

    val updatedDependencies0 = value {
      coursierDeps(
        updatedDependencies,
        scalaArtifactsParamsOpt.map(_.params),
        maybeRecoverOnError
      )
    }
    val runtimeRes = {
      val resolution = fetchRes.resolution.subset(updatedDependencies0.map(_._2._1))
      // this is actually fetcher.artifacts, which is a private field…
      val artifacts = coursier.Artifacts()
        .withCache(fetcher.cache)
        .withClassifiers(fetcher.classifiers)
        .withMainArtifactsOpt(fetcher.mainArtifactsOpt)
        .withArtifactTypesOpt(fetcher.artifactTypesOpt)
        .withExtraArtifactsSeq(fetcher.extraArtifactsSeq)
        .withClasspathOrder(fetcher.classpathOrder)
        .withTransformArtifacts(fetcher.transformArtifacts)
      val res = artifacts
        .withResolution(resolution)
        .runResult()
      res.fullDetailedArtifacts
    }

    val (hasRunner, extraRunnerJars) =
      if (scalaOpt.nonEmpty) {
        val addJvmRunner0 = addJvmRunner.getOrElse(false)
        val runnerJars =
          if (addJvmRunner0) {
            val maybeSnapshotRepo =
              if (runnerVersion.endsWith("SNAPSHOT"))
                Seq(coursier.Repositories.sonatype("snapshots"))
              else Nil
            value {
              artifacts(
                Seq(Positioned.none(
                  dep"$runnerOrganization::$runnerModuleName:$runnerVersion,intransitive"
                )),
                extraRepositories ++ maybeSnapshotRepo,
                scalaArtifactsParamsOpt.map(_.params),
                logger,
                cache.withMessage("Downloading runner dependency")
              ).map(_.map(_._2))
            }
          }
          else
            Nil

        (addJvmRunner0, runnerJars)
      }
      else
        (false, Nil)

    val javacPlugins0 = value {
      javacPluginDependencies
        .map { posDep =>
          val cache0 = cache.withMessage(s"Downloading javac plugin ${posDep.value.render}")
          artifacts(
            Seq(posDep),
            allExtraRepositories,
            scalaArtifactsParamsOpt.map(_.params),
            logger,
            cache0
          )
            .map(_.map { case (url, path) => (posDep.value, url, path) })
        }
        .sequence
        .left.map(CompositeBuildException(_))
        .map(_.flatten)
    }

    Artifacts(
      javacPlugins0,
      extraJavacPlugins,
      defaultDependencies.map(_.value),
      extraDependencies.map(_.value) ++ scalaOpt.toSeq.flatMap(_.extraDependencies),
      compileOnlyDependencies.map(_.value),
      internalDependencies.map(_.value),
      fetchRes.fullDetailedArtifacts.collect { case (d, p, a, Some(f)) =>
        (d, p, a, os.Path(f, Os.pwd))
      },
      runtimeRes.collect { case (d, p, a, Some(f)) =>
        (d, p, a, os.Path(f, Os.pwd))
      },
      extraClassPath,
      extraCompileOnlyJars,
      extraRunnerJars,
      extraSourceJars,
      scalaOpt,
      hasRunner,
      if (keepResolution) Some(fetchRes.resolution) else None
    )
  }

  def scalaPyOrganization(version: String): String = {
    def sortAfterPlus(v: String) = coursier.core.Version(v.replace("+", "-"))
    if (sortAfterPlus(version).compareTo(sortAfterPlus("0.5.2+9-623f0807")) < 0)
      "me.shadaj"
    else
      "dev.scalapy"
  }

  private[build] def artifacts(
    dependencies: Seq[Positioned[AnyDependency]],
    extraRepositories: Seq[Repository],
    paramsOpt: Option[ScalaParameters],
    logger: Logger,
    cache: FileCache[Task],
    classifiersOpt: Option[Set[String]] = None
  ): Either[BuildException, Seq[(String, os.Path)]] = either {
    val res =
      value(fetchAnyDependencies(
        dependencies,
        extraRepositories,
        paramsOpt,
        logger,
        cache,
        classifiersOpt
      ))
    val result = res
      .artifacts
      .iterator
      .map { case (a, f) => (a.url, os.Path(f, Os.pwd)) }
      .toList
    logger.debug {
      val elems = Seq(s"Found ${result.length} artifacts:") ++
        result.map("  " + _._2) ++
        Seq("")
      elems.mkString(System.lineSeparator())
    }
    result
  }

  def coursierDeps(
    dependencies: Seq[Positioned[AnyDependency]],
    paramsOpt: Option[ScalaParameters],
    maybeRecoverOnError: BuildException => Option[BuildException]
  ): Either[BuildException, Seq[Positioned[(
    CsDependency,
    Option[((Module, String), (URL, Boolean))]
  )]]] =
    dependencies
      .map(dep =>
        dep.toCs(paramsOpt)
          .map { case Positioned(pos, csDep) => Positioned(pos, (dep.value, csDep)) }
      )
      .map(_.left.map(maybeRecoverOnError))
      .flatMap {
        case Left(Some(e: NoScalaVersionProvidedError)) => Some(Left(e))
        case Left(_)                                    => None
        case Right(depTuple)                            => Some(Right(depTuple))
      }
      .sequence
      .left.map(CompositeBuildException(_))
      .map(positionedDepTupleSeq =>
        positionedDepTupleSeq.map {
          case Positioned(positions, (dep, csDep)) =>
            val maybeUrl = dep.userParams.get("url").flatten.map(new URL(_))
            val fallback = maybeUrl.map(url => (csDep.module -> csDep.version) -> (url -> true))
            Positioned(positions, (csDep, fallback))
        }
      )

  def fetchAnyDependencies(
    dependencies: Seq[Positioned[AnyDependency]],
    extraRepositories: Seq[Repository],
    paramsOpt: Option[ScalaParameters],
    logger: Logger,
    cache: FileCache[Task],
    classifiersOpt: Option[Set[String]],
    maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e)
  ): Either[BuildException, Fetch.Result] = either {
    val (_, res) = value {
      fetchAnyDependenciesWithResult(
        dependencies,
        extraRepositories,
        paramsOpt,
        logger,
        cache,
        classifiersOpt,
        maybeRecoverOnError
      )
    }
    res
  }

  private def fetchAnyDependenciesWithResult(
    dependencies: Seq[Positioned[AnyDependency]],
    extraRepositories: Seq[Repository],
    paramsOpt: Option[ScalaParameters],
    logger: Logger,
    cache: FileCache[Task],
    classifiersOpt: Option[Set[String]],
    maybeRecoverOnError: BuildException => Option[BuildException]
  ): Either[BuildException, (coursier.Fetch[Task], coursier.Fetch.Result)] = either {
    val coursierDependenciesWithFallbacks = value {
      coursierDeps(dependencies, paramsOpt, maybeRecoverOnError)
    }

    val coursierDependencies: Seq[Positioned[CsDependency]] =
      coursierDependenciesWithFallbacks.map(_.map(_._1))
    val fallbacks: Map[(Module, String), (URL, Boolean)] =
      coursierDependenciesWithFallbacks.map(_.value)
        .flatMap(_._2)
        .toMap

    value {
      fetchCsDependencies(
        coursierDependencies,
        extraRepositories,
        paramsOpt.map(_.scalaVersion),
        Nil,
        logger,
        cache,
        classifiersOpt,
        fallbacks
      ).left.flatMap(_.maybeRecoverWithDefault(
        (
          fetcher(
            coursierDependencies,
            extraRepositories,
            paramsOpt.map(_.scalaVersion),
            Nil,
            cache,
            classifiersOpt,
            fallbacks
          ),
          Fetch.Result()
        ),
        maybeRecoverOnError
      ))
    }
  }

  private def fetcher(
    dependencies: Seq[Positioned[coursier.Dependency]],
    extraRepositories: Seq[Repository],
    forceScalaVersionOpt: Option[String],
    forcedVersions: Seq[(coursier.Module, String)],
    cache: FileCache[Task],
    classifiersOpt: Option[Set[String]],
    fallbacks: Map[(Module, String), (URL, Boolean)]
  ): coursier.Fetch[Task] = {

    val fallbackRepository            = TemporaryInMemoryRepository(fallbacks)
    val extraRepositoriesWithFallback = extraRepositories :+ fallbackRepository

    val forceScalaVersions = forceScalaVersionOpt match {
      case None => Nil
      case Some(sv) =>
        if (sv.startsWith("2."))
          Seq(
            cmod"org.scala-lang:scala-library"  -> sv,
            cmod"org.scala-lang:scala-compiler" -> sv,
            cmod"org.scala-lang:scala-reflect"  -> sv
          )
        else
          // FIXME Shouldn't we force the org.scala-lang:scala-library version too?
          // (to a 2.13.x version)
          Seq(
            cmod"org.scala-lang:scala3-library_3"         -> sv,
            cmod"org.scala-lang:scala3-compiler_3"        -> sv,
            cmod"org.scala-lang:scala3-interfaces_3"      -> sv,
            cmod"org.scala-lang:scala3-tasty-inspector_3" -> sv,
            cmod"org.scala-lang:tasty-core_3"             -> sv
          )
    }

    val forceVersion = forceScalaVersions ++ forcedVersions

    // FIXME Many parameters that we could allow to customize here
    val defaultFetcher = coursier.Fetch()
    var fetcher = defaultFetcher
      .withCache(cache)
      // repository order matters here, since in some cases coursier resolves only the head
      .withRepositories(extraRepositoriesWithFallback ++ defaultFetcher.repositories)
      .addDependencies(dependencies.map(_.value)*)
      .mapResolutionParams(_.addForceVersion(forceVersion*))
    for (classifiers <- classifiersOpt) {
      if (classifiers("_"))
        fetcher = fetcher.withMainArtifacts()
      fetcher = fetcher
        .addClassifiers(classifiers.toSeq.filter(_ != "_").map(coursier.Classifier(_))*)
    }
    fetcher
  }

  def fetchCsDependencies(
    dependencies: Seq[Positioned[coursier.Dependency]],
    extraRepositories: Seq[Repository],
    forceScalaVersionOpt: Option[String],
    forcedVersions: Seq[(coursier.Module, String)],
    logger: Logger,
    cache: FileCache[Task],
    classifiersOpt: Option[Set[String]],
    fallbacks: Map[(Module, String), (URL, Boolean)] = Map.empty
  ): Either[BuildException, (coursier.Fetch[Task], coursier.Fetch.Result)] = either {
    logger.debug {
      s"Fetching ${dependencies.map(_.value)}" +
        (if (extraRepositories.isEmpty) "" else s", adding $extraRepositories")
    }

    val fetcher0 = fetcher(
      dependencies,
      extraRepositories,
      forceScalaVersionOpt,
      forcedVersions,
      cache,
      classifiersOpt,
      fallbacks
    )

    val res = cache.logger.use {
      fetcher0.eitherResult()
    }
    value {
      res.left.map {
        case ex: ResolutionError.Several =>
          CompositeBuildException(
            ex.errors.map(toFetchingDependenciesError(dependencies, _))
          )
        case ex: ResolutionError.Simple =>
          toFetchingDependenciesError(dependencies, ex)
        case ex => new FetchingDependenciesError(ex, dependencies.flatMap(_.positions))
      }.map((fetcher0, _))
    }
  }

  def toFetchingDependenciesError(
    dependencies: Seq[Positioned[coursier.Dependency]],
    resolutionError: coursier.error.ResolutionError.Simple
  ) = resolutionError match {
    case ex: ResolutionError.CantDownloadModule
        if ex.module.name.value == s"${Constants.toolkitName}_2.12" || ex.module.name
          .value == s"${Constants.toolkitTestName}_2.12" =>
      val errorPositions = dependencies.collect {
        case Positioned(pos, dep)
            if ex.module == dep.module => pos
      }.flatten
      new ToolkitVersionError(
        "Toolkits do not support Scala 2.12",
        errorPositions
      )
    case ex: ResolutionError.CantDownloadModule =>
      val errorPositions = dependencies.collect {
        case Positioned(pos, dep)
            if ex.module == dep.module => pos
      }.flatten
      new FetchingDependenciesError(ex, errorPositions)
    case ex => new FetchingDependenciesError(ex, dependencies.flatMap(_.positions))
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy