Add support for fallback artifact in SBT plugin

This commit is contained in:
Alexandre Archambault 2016-04-01 00:39:31 +02:00
parent 4b3659bd0f
commit 3367f49058
10 changed files with 195 additions and 10 deletions

View File

@ -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
)

View File

@ -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),

View File

@ -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)))
}
}

View File

@ -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],

View File

@ -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", "")

View File

@ -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)

View File

@ -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
}

View File

@ -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)
}

View File

@ -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"))
}

View File

@ -0,0 +1,3 @@
$ delete output
> run
$ exists output