diff --git a/build.sbt b/build.sbt index e22661eb4..dc7b5a0ef 100644 --- a/build.sbt +++ b/build.sbt @@ -413,7 +413,8 @@ lazy val plugin = project scriptedLaunchOpts ++= Seq( "-Xmx1024M", "-XX:MaxPermSize=256M", - "-Dplugin.version=" + version.value + "-Dplugin.version=" + version.value, + "-Dsbttest.base=" + (sourceDirectory.value / "sbt-test").getAbsolutePath ), scriptedBufferLog := false ) diff --git a/plugin/src/main/scala-2.10/coursier/CoursierPlugin.scala b/plugin/src/main/scala-2.10/coursier/CoursierPlugin.scala index 01db819d8..308295082 100644 --- a/plugin/src/main/scala-2.10/coursier/CoursierPlugin.scala +++ b/plugin/src/main/scala-2.10/coursier/CoursierPlugin.scala @@ -20,6 +20,7 @@ object CoursierPlugin extends AutoPlugin { val coursierVerbosity = Keys.coursierVerbosity val coursierResolvers = Keys.coursierResolvers val coursierSbtResolvers = Keys.coursierSbtResolvers + val coursierFallbackDependencies = Keys.coursierFallbackDependencies val coursierCache = Keys.coursierCache val coursierProject = Keys.coursierProject val coursierProjects = Keys.coursierProjects @@ -39,6 +40,7 @@ object CoursierPlugin extends AutoPlugin { coursierVerbosity := Settings.defaultVerbosityLevel, coursierResolvers <<= Tasks.coursierResolversTask, coursierSbtResolvers <<= externalResolvers in updateSbtClassifiers, + coursierFallbackDependencies <<= Tasks.coursierFallbackDependenciesTask, coursierCache := Cache.default, update <<= Tasks.updateTask(withClassifiers = false), updateClassifiers <<= Tasks.updateTask(withClassifiers = true), diff --git a/plugin/src/main/scala-2.10/coursier/FallbackDependenciesRepository.scala b/plugin/src/main/scala-2.10/coursier/FallbackDependenciesRepository.scala new file mode 100644 index 000000000..3c196e9d6 --- /dev/null +++ b/plugin/src/main/scala-2.10/coursier/FallbackDependenciesRepository.scala @@ -0,0 +1,55 @@ +package coursier + +import java.net.URL + +import scalaz.{ EitherT, Monad } + +case class FallbackDependenciesRepository( + fallbacks: Map[(Module, String), (URL, Boolean)] +) extends Repository { + + private val source = new Artifact.Source { + def artifacts( + dependency: Dependency, + project: Project, + overrideClassifiers: Option[Seq[String]] + ) = + fallbacks.get(dependency.moduleVersion) match { + case None => Nil + case Some((url, changing)) => + Seq( + Artifact(url.toString, Map.empty, Map.empty, Attributes("jar", ""), changing) + ) + } + } + + def find[F[_]]( + module: Module, + version: String, + fetch: Fetch.Content[F] + )(implicit + F: Monad[F] + ): EitherT[F, String, (Artifact.Source, Project)] = + fallbacks.get((module, version)) match { + case None => + EitherT.left(F.point("No fallback URL found")) + + case Some((url, _)) => + val proj = Project( + module, + version, + Nil, + Map.empty, + None, + Nil, + Nil, + Nil, + None, + None, + Nil, + Info.empty + ) + + EitherT.right(F.point((source, proj))) + } +} diff --git a/plugin/src/main/scala-2.10/coursier/FromSbt.scala b/plugin/src/main/scala-2.10/coursier/FromSbt.scala index a17bad9c8..367c090f7 100644 --- a/plugin/src/main/scala-2.10/coursier/FromSbt.scala +++ b/plugin/src/main/scala-2.10/coursier/FromSbt.scala @@ -2,7 +2,7 @@ package coursier import coursier.ivy.{ IvyXml, IvyRepository } -import java.net.MalformedURLException +import java.net.{ MalformedURLException, URL } import sbt.{ Resolver, CrossVersion, ModuleID } import sbt.mavenint.SbtPomExtraProperties @@ -85,6 +85,20 @@ object FromSbt { } yield from -> dep.copy(configuration = to, attributes = attr) } + def fallbackDependencies( + allDependencies: Seq[ModuleID], + scalaVersion: String, + scalaBinaryVersion: String + ): Seq[(Module, String, URL, Boolean)] = + for { + module <- allDependencies + artifact <- module.explicitArtifacts + url <- artifact.url.toSeq + } yield { + val (module0, version) = moduleVersion(module, scalaVersion, scalaBinaryVersion) + (module0, version, url, module.isChanging) + } + def project( projectID: ModuleID, allDependencies: Seq[ModuleID], diff --git a/plugin/src/main/scala-2.10/coursier/Keys.scala b/plugin/src/main/scala-2.10/coursier/Keys.scala index 0af9153d9..796031c87 100644 --- a/plugin/src/main/scala-2.10/coursier/Keys.scala +++ b/plugin/src/main/scala-2.10/coursier/Keys.scala @@ -1,6 +1,8 @@ package coursier import java.io.File +import java.net.URL + import coursier.core.Publication import sbt.{ GetClassifiersModule, Resolver, SettingKey, TaskKey } @@ -18,6 +20,8 @@ object Keys { val coursierCache = SettingKey[File]("coursier-cache", "") + val coursierFallbackDependencies = TaskKey[Seq[(Module, String, URL, Boolean)]]("coursier-fallback-dependencies", "") + val coursierProject = TaskKey[Project]("coursier-project", "") val coursierProjects = TaskKey[Seq[Project]]("coursier-projects", "") val coursierPublications = TaskKey[Seq[(String, Publication)]]("coursier-publications", "") diff --git a/plugin/src/main/scala-2.10/coursier/Tasks.scala b/plugin/src/main/scala-2.10/coursier/Tasks.scala index fa56b03c5..2d1569a28 100644 --- a/plugin/src/main/scala-2.10/coursier/Tasks.scala +++ b/plugin/src/main/scala-2.10/coursier/Tasks.scala @@ -1,6 +1,7 @@ package coursier import java.io.{ OutputStreamWriter, File } +import java.net.URL import java.nio.file.Files import java.util.concurrent.Executors @@ -42,6 +43,26 @@ object Tasks { } } + def coursierFallbackDependenciesTask: Def.Initialize[sbt.Task[Seq[(Module, String, URL, Boolean)]]] = + ( + sbt.Keys.state, + sbt.Keys.thisProjectRef + ).flatMap { (state, projectRef) => + + val allDependenciesTask = allDependencies.in(projectRef).get(state) + + for { + allDependencies <- allDependenciesTask + } yield { + + FromSbt.fallbackDependencies( + allDependencies, + scalaVersion.in(projectRef).get(state), + scalaBinaryVersion.in(projectRef).get(state) + ) + } + } + def coursierProjectTask: Def.Initialize[sbt.Task[Project]] = ( sbt.Keys.state, @@ -181,19 +202,31 @@ object Tasks { lazy val cm = coursierSbtClassifiersModule.value - val currentProject = - if (sbtClassifiers) - FromSbt.project( + val (currentProject, fallbackDependencies) = + if (sbtClassifiers) { + val sv = scalaVersion.value + val sbv = scalaBinaryVersion.value + + val proj = FromSbt.project( cm.id, cm.modules, cm.configurations.map(cfg => cfg.name -> cfg.extendsConfigs.map(_.name)).toMap, - scalaVersion.value, - scalaBinaryVersion.value + sv, + sbv ) - else { + + val fallbackDeps = FromSbt.fallbackDependencies( + cm.modules, + sv, + sbv + ) + + (proj, fallbackDeps) + } else { val proj = coursierProject.value val publications = coursierPublications.value - proj.copy(publications = publications) + val fallbackDeps = coursierFallbackDependencies.value + (proj.copy(publications = publications), fallbackDeps) } val ivySbt0 = ivySbt.value @@ -277,7 +310,25 @@ object Tasks { "ivy.home" -> (new File(sys.props("user.home")).toURI.getPath + ".ivy2") ) ++ sys.props - val repositories = Seq(globalPluginsRepo, interProjectRepo) ++ resolvers.flatMap(FromSbt.repository(_, ivyProperties)) + val repositories = Seq( + globalPluginsRepo, + interProjectRepo + ) ++ resolvers.flatMap( + FromSbt.repository(_, ivyProperties) + ) ++ { + if (fallbackDependencies.isEmpty) + Nil + else { + val map = fallbackDependencies.map { + case (mod, ver, url, changing) => + (mod, ver) -> ((url, changing)) + }.toMap + + Seq( + FallbackDependenciesRepository(map) + ) + } + } def report = { val pool = Executors.newFixedThreadPool(parallelDownloads, Strategy.DefaultDaemonThreadFactory) diff --git a/plugin/src/sbt-test/sbt-coursier/from/build.sbt b/plugin/src/sbt-test/sbt-coursier/from/build.sbt new file mode 100644 index 000000000..0f861ab23 --- /dev/null +++ b/plugin/src/sbt-test/sbt-coursier/from/build.sbt @@ -0,0 +1,31 @@ +scalaVersion := "2.11.8" + +libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.41" from { + + val f = file(sys.props("sbttest.base")) / "sbt-coursier" / "from" / "shapeless_2.11-2.3.0.jar" + + if (!f.exists()) { + val url0 = "https://repo1.maven.org/maven2/com/chuusai/shapeless_2.11/2.3.0/shapeless_2.11-2.3.0.jar" + + scala.Console.err.println(s"Fetching $url0") + + val url = new java.net.URL(url0) + + val is = url.openStream() + val os = new java.io.FileOutputStream(f) + + var read = -1 + val b = Array.fill[Byte](16*1024)(0) + while ({ + read = is.read(b) + read >= 0 + }) { + os.write(b, 0, read) + } + + is.close() + os.close() + } + + f.toURI.toString +} \ No newline at end of file diff --git a/plugin/src/sbt-test/sbt-coursier/from/project/plugins.sbt b/plugin/src/sbt-test/sbt-coursier/from/project/plugins.sbt new file mode 100644 index 000000000..152225a9e --- /dev/null +++ b/plugin/src/sbt-test/sbt-coursier/from/project/plugins.sbt @@ -0,0 +1,11 @@ +{ + val pluginVersion = sys.props.getOrElse( + "plugin.version", + throw new RuntimeException( + """|The system property 'plugin.version' is not defined. + |Specify this property using the scriptedLaunchOpts -D.""".stripMargin + ) + ) + + addSbtPlugin("io.get-coursier" % "sbt-coursier" % pluginVersion) +} diff --git a/plugin/src/sbt-test/sbt-coursier/from/src/main/scala/Main.scala b/plugin/src/sbt-test/sbt-coursier/from/src/main/scala/Main.scala new file mode 100644 index 000000000..c9401ff3e --- /dev/null +++ b/plugin/src/sbt-test/sbt-coursier/from/src/main/scala/Main.scala @@ -0,0 +1,13 @@ +import java.io.File +import java.nio.file.Files + +import shapeless._ + +object Main extends App { + case class CC(s: String) + val cc = CC("OK") + val l = Generic[CC].to(cc) + val msg = l.head + + Files.write(new File("output").toPath, msg.getBytes("UTF-8")) +} diff --git a/plugin/src/sbt-test/sbt-coursier/from/test b/plugin/src/sbt-test/sbt-coursier/from/test new file mode 100644 index 000000000..2182f57b0 --- /dev/null +++ b/plugin/src/sbt-test/sbt-coursier/from/test @@ -0,0 +1,3 @@ +$ delete output +> run +$ exists output