Skip to content

Commit

Permalink
Make dependencies keep their positions when fetching
Browse files Browse the repository at this point in the history
Add ResolutionError handling
  • Loading branch information
MaciejG604 committed Jul 5, 2023
1 parent a7b887a commit e0ab571
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 45 deletions.
2 changes: 1 addition & 1 deletion modules/build/src/main/scala/scala/build/Bloop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ object Bloop {
either {
val res = value {
Artifacts.artifacts(
Positioned.none(Seq(dep)),
Seq(Positioned.none(dep)),
Nil,
Some(params),
logger,
Expand Down
6 changes: 3 additions & 3 deletions modules/build/src/main/scala/scala/build/ReplArtifacts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ object ReplArtifacts {
addScalapy.map(ver => dep"${Artifacts.scalaPyOrganization(ver)}::scalapy-core::$ver").toSeq
val allDeps = dependencies ++ Seq(dep"com.lihaoyi:::ammonite:$ammoniteVersion") ++ scalapyDeps
val replArtifacts = Artifacts.artifacts(
Positioned.none(allDeps),
allDeps.map(Positioned.none),
extraRepositories,
Some(scalaParams),
logger,
cache.withMessage(s"Downloading Ammonite $ammoniteVersion")
)
val replSourceArtifacts = Artifacts.artifacts(
Positioned.none(allDeps),
allDeps.map(Positioned.none),
extraRepositories,
Some(scalaParams),
logger,
Expand Down Expand Up @@ -95,7 +95,7 @@ object ReplArtifacts {
val allDeps = dependencies ++ Seq(replDep) ++ scalapyDeps
val replArtifacts =
Artifacts.artifacts(
Positioned.none(allDeps),
allDeps.map(Positioned.none),
repositories,
Some(scalaParams),
logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import scala.build.tests.util.BloopServer
import build.Ops.EitherThrowOps
import dependency.AnyDependency

import scala.build.errors.{DependencyFormatError, ToolkitDirectiveMissingVersionError}
import scala.build.errors.{
CompositeBuildException,
DependencyFormatError,
FetchingDependenciesError,
ToolkitDirectiveMissingVersionError
}

class DirectiveTests extends munit.FunSuite {

Expand Down Expand Up @@ -421,4 +426,46 @@ class DirectiveTests extends munit.FunSuite {
}
}
}

test("dependency resolution errors with positions") {
val testInputs = TestInputs(
os.rel / "simple.sc" ->
"""//> using dep org.xyz::foo:0.0.1
|//> using dep org.qwerty::bar:0.0.1
|""".stripMargin
)
testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {
(root, _, maybeBuild) =>
expect(maybeBuild.isLeft)
val errors = maybeBuild.left.toOption.get

errors match {
case error: CompositeBuildException =>
expect(error.exceptions.length == 2)
expect(error.exceptions.forall(_.isInstanceOf[FetchingDependenciesError]))
expect(error.exceptions.forall(_.positions.length == 1))

{
val xyzError = error.exceptions.find(_.message.contains("org.xyz")).get
expect(xyzError.message.startsWith("Error downloading org.xyz:foo"))
expect(xyzError.positions.head == Position.File(
Right(root / "simple.sc"),
(0, 14),
(0, 32)
))
}

{
val qwertyError = error.exceptions.find(_.message.contains("org.qwerty")).get
expect(qwertyError.message.contains("Error downloading org.qwerty:bar"))
expect(qwertyError.positions.head == Position.File(
Right(root / "simple.sc"),
(1, 14),
(1, 35)
))
}
case _ => fail("unexpected BuildException type")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ abstract class ScalaCommand[T <: HasGlobalOptions](implicit myParser: Parser[T],
if (shared.helpGroups.helpScaladoc) {
val docArtifacts = value {
Artifacts.fetch(
Positioned.none(Seq(dep"org.scala-lang::scaladoc:${scalaParams.scalaVersion}")),
Seq(Positioned.none(dep"org.scala-lang::scaladoc:${scalaParams.scalaVersion}")),
value(buildOptions.finalRepositories),
Some(scalaParams),
logger,
Expand Down Expand Up @@ -246,7 +246,7 @@ abstract class ScalaCommand[T <: HasGlobalOptions](implicit myParser: Parser[T],
else {
val fmtArtifacts = value {
Artifacts.fetch(
Positioned.none(Seq(
Seq(Positioned.none(
dep"${Constants.scalafmtOrganization}:${Constants.scalafmtName}:${Constants.defaultScalafmtVersion}"
)),
value(buildOptions.finalRepositories),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ object Doc extends ScalaCommand[DocOptions] {
case Some(scalaParams) =>
val res = value {
Artifacts.fetch(
Positioned.none(Seq(dep"org.scala-lang::scaladoc:${scalaParams.scalaVersion}")),
Seq(Positioned.none(dep"org.scala-lang::scaladoc:${scalaParams.scalaVersion}")),
value(build.options.finalRepositories),
Some(scalaParams),
logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object New extends ScalaCommand[NewOptions] {
override def runCommand(options: NewOptions, remainingArgs: RemainingArgs, logger: Logger): Unit =
val scalaParameters = ScalaParameters(Constants.defaultScala213Version)
val fetchedGiter8 = Artifacts.fetch(
Positioned.none(giter8Dependency),
giter8Dependency.map(Positioned.none),
Seq.empty,
Some(scalaParameters),
logger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {
if (build.options.notForBloopOptions.doSetupPython.getOrElse(false)) {
val res = value {
Artifacts.fetch(
Positioned.none(Seq(
Seq(Positioned.none(
dep"${Constants.pythonInterfaceOrg}:${Constants.pythonInterfaceName}:${Constants.pythonInterfaceVersion}"
)),
Nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ object PgpExternalCommand {

val signingClassPath = value {
scala.build.Artifacts.fetch0(
Positioned.none(Seq(jvmSigningDep.toCs)),
Seq(Positioned.none(jvmSigningDep.toCs)),
extraRepos,
None,
Nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ object ScalaJsLinker {
case Right(()) =>
val linkerClassPath = value {
scala.build.Artifacts.fetch0(
Positioned.none(Seq(scalaJsCliDep.toCs)),
Seq(Positioned.none(scalaJsCliDep.toCs)),
extraRepos,
None,
forcedVersions.map { case (m, v) => (m.toCs, v) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object LauncherCli {

val fetchedScalaCli =
Artifacts.fetch(
Positioned.none(scalaCliDependency),
scalaCliDependency.map(Positioned.none),
snapshotsRepo,
Some(scalaParameters),
logger,
Expand Down
92 changes: 60 additions & 32 deletions modules/options/src/main/scala/scala/build/Artifacts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package scala.build

import coursier.cache.FileCache
import coursier.core.{Classifier, Module, ModuleName, Organization, Repository, Version}
import coursier.error.{CoursierError, ResolutionError}
import coursier.parse.RepositoryParser
import coursier.util.Task
import coursier.{Dependency => CsDependency, Fetch, Resolution, core => csCore, util => csUtil}
Expand Down Expand Up @@ -139,7 +140,7 @@ object Artifacts {
val posDep0 =
posDep.map(dep => dep.copy(userParams = dep.userParams + ("intransitive" -> None)))
artifacts(
posDep0.map(Seq(_)),
Seq(posDep0),
allExtraRepositories,
Some(scalaArtifactsParams.params),
logger,
Expand All @@ -156,7 +157,7 @@ object Artifacts {

val compilerArtifacts = value {
artifacts(
Positioned.none(compilerDependencies),
compilerDependencies.map(Positioned.none),
allExtraRepositories,
Some(scalaArtifactsParams.params),
logger,
Expand Down Expand Up @@ -185,7 +186,7 @@ object Artifacts {
Some(
value {
fetch0(
Positioned.none(dependency),
dependency.map(Positioned.none),
allExtraRepositories,
None,
forcedVersions,
Expand All @@ -212,7 +213,7 @@ object Artifacts {
Some(
value {
fetch0(
Positioned.none(dependency),
dependency.map(Positioned.none),
allExtraRepositories,
None,
Nil,
Expand Down Expand Up @@ -299,7 +300,7 @@ object Artifacts {

val fetchRes = value {
fetch(
Positioned.sequence(updatedDependencies),
updatedDependencies,
allExtraRepositories,
scalaArtifactsParamsOpt.map(_.params),
logger,
Expand All @@ -320,9 +321,9 @@ object Artifacts {
else Nil
value {
artifacts(
Positioned.none(
Seq(dep"$runnerOrganization::$runnerModuleName:$runnerVersion,intransitive")
),
Seq(Positioned.none(
dep"$runnerOrganization::$runnerModuleName:$runnerVersion,intransitive"
)),
extraRepositories ++ maybeSnapshotRepo,
scalaArtifactsParamsOpt.map(_.params),
logger,
Expand All @@ -343,7 +344,7 @@ object Artifacts {
.map { posDep =>
val cache0 = cache.withMessage(s"Downloading javac plugin ${posDep.value.render}")
artifacts(
posDep.map(Seq(_)),
Seq(posDep),
allExtraRepositories,
scalaArtifactsParamsOpt.map(_.params),
logger,
Expand Down Expand Up @@ -383,7 +384,7 @@ object Artifacts {
}

private[build] def artifacts(
dependencies: Positioned[Seq[AnyDependency]],
dependencies: Seq[Positioned[AnyDependency]],
extraRepositories: Seq[Repository],
paramsOpt: Option[ScalaParameters],
logger: Logger,
Expand All @@ -407,40 +408,46 @@ object Artifacts {
}

def fetch(
dependencies: Positioned[Seq[AnyDependency]],
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 coursierDependenciesWithFallbacks = value {
dependencies.value
.map(Positioned(dependencies.positions, _))
.map(dep => dep.toCs(paramsOpt).map(csDep => (dep.value, csDep.value)))
val coursierDependenciesWithFallbacks
: Seq[Positioned[(CsDependency, Option[((Module, String), (URL, Boolean))])]] = value {
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(dep) => Some(Right(dep))
case Right(depTuple) => Some(Right(depTuple))
}
.sequence
.left.map(CompositeBuildException(_))
.map(_.map {
case (dep, csDep) =>
val maybeUrl = dep.userParams.get("url").flatten.map(new URL(_))
val fallback = maybeUrl.map(url => (csDep.module -> csDep.version) -> (url -> true))
(csDep, fallback)
})
.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))
}
)
}
val coursierDependenciesWithFallbacks0
: Positioned[Seq[(CsDependency, Option[((Module, String), (URL, Boolean))])]] =
dependencies.map(_ => coursierDependenciesWithFallbacks)
val coursierDependencies: Positioned[Seq[CsDependency]] =
coursierDependenciesWithFallbacks0.map(_.map(_._1))

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

value {
fetch0(
coursierDependencies,
Expand All @@ -456,7 +463,7 @@ object Artifacts {
}

def fetch0(
dependencies: Positioned[Seq[coursier.Dependency]],
dependencies: Seq[Positioned[coursier.Dependency]],
extraRepositories: Seq[Repository],
forceScalaVersionOpt: Option[String],
forcedVersions: Seq[(coursier.Module, String)],
Expand All @@ -466,7 +473,7 @@ object Artifacts {
fallbacks: Map[(Module, String), (URL, Boolean)] = Map.empty
): Either[BuildException, Fetch.Result] = either {
logger.debug {
s"Fetching ${dependencies.value}" +
s"Fetching ${dependencies.map(_.value)}" +
(if (extraRepositories.isEmpty) "" else s", adding $extraRepositories")
}

Expand Down Expand Up @@ -500,7 +507,7 @@ object Artifacts {
var fetcher = coursier.Fetch()
.withCache(cache)
.addRepositories(extraRepositoriesWithFallback*)
.addDependencies(dependencies.value*)
.addDependencies(dependencies.map(_.value)*)
.mapResolutionParams(_.addForceVersion(forceVersion*))
for (classifiers <- classifiersOpt) {
if (classifiers("_"))
Expand All @@ -513,7 +520,28 @@ object Artifacts {
fetcher.eitherResult()
}
value {
res.left.map(ex => new FetchingDependenciesError(ex, dependencies.positions))
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))
}
}
}

def toFetchingDependenciesError(
dependencies: Seq[Positioned[coursier.Dependency]],
resolutionError: coursier.error.ResolutionError.Simple
) = resolutionError match {
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))
}
}

0 comments on commit e0ab571

Please sign in to comment.