From 2ecfb282365f7fd8168bfb845cdbb403c00e9615 Mon Sep 17 00:00:00 2001 From: Grant Date: Mon, 18 Dec 2017 19:57:15 -0800 Subject: Report errors when dependency resolution fails (#71) * Report errors when dependency resolution fails Function `Lib.reolveDependencies` now checks the result of the coursier dependency resolution for errors. The return value of the fuction is now a `Result[Seq[PathRef]]` and errors are signaled by returning an instance of Failure when errors occur while resolving dependencies. `ScalaModule` now has a new task called `resolve` which allows for triggering dependency resolution directly from the cmd line like: `mill run resolve`. `ResolveDepsTests` was added to verify failed resolution is properly detected and reported. * Remove type signatures in ScalaModule.scala * Updates from PR comments --- .../src/main/scala/mill/scalaplugin/Lib.scala | 39 +++++++++++++++------- .../main/scala/mill/scalaplugin/ScalaModule.scala | 12 ++++--- .../resolve-deps/src/main/scala/Main.scala | 3 ++ .../scala/mill/scalaplugin/ResolveDepsTests.scala | 39 ++++++++++++++++++++++ 4 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 scalaplugin/src/test/resource/resolve-deps/src/main/scala/Main.scala create mode 100644 scalaplugin/src/test/scala/mill/scalaplugin/ResolveDepsTests.scala (limited to 'scalaplugin') diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala b/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala index 637906a5..c89254b0 100644 --- a/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala +++ b/scalaplugin/src/main/scala/mill/scalaplugin/Lib.scala @@ -6,7 +6,7 @@ import java.net.URLClassLoader import java.util.Optional import ammonite.ops._ -import coursier.{Cache, Fetch, MavenRepository, Repository, Resolution} +import coursier.{Cache, Fetch, MavenRepository, Repository, Resolution, Module => CoursierModule} import mill.define.Worker import mill.eval.{PathRef, Result} import mill.util.Ctx @@ -159,7 +159,7 @@ object Lib{ scalaVersion: String, scalaBinaryVersion: String, deps: Seq[Dep], - sources: Boolean = false): Seq[PathRef] = { + sources: Boolean = false): Result[Seq[PathRef]] = { val flattened = deps.map{ case Dep.Java(dep) => dep case Dep.Scala(dep) => @@ -171,15 +171,30 @@ object Lib{ val fetch = Fetch.from(repositories, Cache.fetch()) val resolution = start.process.run(fetch).unsafePerformSync - val sourceOrJar = - if (sources) resolution.classifiersArtifacts(Seq("sources")) - else resolution.artifacts - val localArtifacts: Seq[File] = scalaz.concurrent.Task - .gatherUnordered(sourceOrJar.map(Cache.file(_).run)) - .unsafePerformSync - .flatMap(_.toOption) - - localArtifacts.map(p => PathRef(Path(p), quick = true)) + val errs = resolution.metadataErrors + if(errs.nonEmpty) { + val header = + s"""| + |Resolution failed for ${errs.length} modules: + |-------------------------------------------- + |""".stripMargin + + val errLines = errs.map { + case ((module, vsn), errMsgs) => s" ${module.trim}:$vsn \n\t" + errMsgs.mkString("\n\t") + }.mkString("\n") + val msg = header + errLines + "\n" + Result.Failure(msg) + } else { + val sourceOrJar = + if (sources) resolution.classifiersArtifacts(Seq("sources")) + else resolution.artifacts + val localArtifacts: Seq[File] = scalaz.concurrent.Task + .gatherUnordered(sourceOrJar.map(Cache.file(_).run)) + .unsafePerformSync + .flatMap(_.toOption) + + localArtifacts.map(p => PathRef(Path(p), quick = true)) + } } def scalaCompilerIvyDeps(scalaVersion: String) = Seq( Dep.Java("org.scala-lang", "scala-compiler", scalaVersion), @@ -195,4 +210,4 @@ object Lib{ "#!/usr/bin/env sh", "exec java -jar \"$0\" \"$@\"" ) -} \ No newline at end of file +} diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala index 9f9425a4..ce7b5e00 100644 --- a/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala +++ b/scalaplugin/src/main/scala/mill/scalaplugin/ScalaModule.scala @@ -2,13 +2,12 @@ package mill package scalaplugin import ammonite.ops._ -import coursier.{Cache, MavenRepository, Repository, Resolution} -import mill.define.Task +import coursier.{Cache, MavenRepository, Repository} +import mill.define.{Source, Task} import mill.define.Task.{Module, TaskModule} import mill.eval.{PathRef, Result} import mill.modules.Jvm import mill.modules.Jvm.{createAssembly, createJar, interactiveSubprocess, subprocess} - import Lib._ trait TestScalaModule extends ScalaModule with TaskModule { override def defaultCommandName() = "test" @@ -101,6 +100,7 @@ trait ScalaModule extends Module with TaskModule{ outer => sources ) } + def externalCompileDepClasspath: T[Seq[PathRef]] = T{ Task.traverse(projectDeps)(_.externalCompileDepClasspath)().flatten ++ resolveDeps( @@ -115,6 +115,7 @@ trait ScalaModule extends Module with TaskModule{ outer => sources = true )() } + /** * Things that need to be on the classpath in order for this code to compile; * might be less than the runtime classpath @@ -140,8 +141,9 @@ trait ScalaModule extends Module with TaskModule{ outer => Seq(dep) ) classpath match { - case Seq(single) => PathRef(single.path, quick = true) - case Seq() => throw new Exception(dep + " resolution failed") + case Result.Success(Seq(single)) => PathRef(single.path, quick = true) + case Result.Success(Seq()) => throw new Exception(dep + " resolution failed") + case f: Result.Failure => throw new Exception(dep + s" resolution failed.\n + ${f.msg}") case _ => throw new Exception(dep + " resolution resulted in more than one file") } } diff --git a/scalaplugin/src/test/resource/resolve-deps/src/main/scala/Main.scala b/scalaplugin/src/test/resource/resolve-deps/src/main/scala/Main.scala new file mode 100644 index 00000000..5dcbe39a --- /dev/null +++ b/scalaplugin/src/test/resource/resolve-deps/src/main/scala/Main.scala @@ -0,0 +1,3 @@ +object Main { + println("ResolveDeps Main: hello world!") +} diff --git a/scalaplugin/src/test/scala/mill/scalaplugin/ResolveDepsTests.scala b/scalaplugin/src/test/scala/mill/scalaplugin/ResolveDepsTests.scala new file mode 100644 index 00000000..cd6bd40f --- /dev/null +++ b/scalaplugin/src/test/scala/mill/scalaplugin/ResolveDepsTests.scala @@ -0,0 +1,39 @@ +package mill.scalaplugin + +import coursier.Cache +import coursier.maven.MavenRepository +import mill.eval.Result.{Failure, Success} +import mill.eval.{PathRef, Result} +import utest._ + +object ResolveDepsTests extends TestSuite { + val repos = Seq(Cache.ivy2Local, MavenRepository("https://repo1.maven.org/maven2")) + + def evalDeps(deps: Seq[Dep]): Result[Seq[PathRef]] = Lib.resolveDependencies(repos, "2.12.4", "2.12", deps) + + val tests = Tests { + 'resolveValidDeps - { + val deps = Seq(Dep("com.lihaoyi", "pprint", "0.5.3")) + val Success(paths) = evalDeps(deps) + assert(paths.nonEmpty) + } + + 'errOnInvalidOrgDeps - { + val deps = Seq(Dep("xxx.yyy.invalid", "pprint", "0.5.3")) + val Failure(errMsg) = evalDeps(deps) + assert(errMsg.contains("xxx.yyy.invalid")) + } + + 'errOnInvalidVersionDeps - { + val deps = Seq(Dep("com.lihaoyi", "pprint", "invalid.version.num")) + val Failure(errMsg) = evalDeps(deps) + assert(errMsg.contains("invalid.version.num")) + } + + 'errOnPartialSuccess - { + val deps = Seq(Dep("com.lihaoyi", "pprint", "0.5.3"), Dep("fake", "fake", "fake")) + val Failure(errMsg) = evalDeps(deps) + assert(errMsg.contains("fake")) + } + } +} -- cgit v1.2.3