Support for enhanced cross build suffix in dependencies. Closes #267

Overloading `%%` for library dependency to allow using a library built
with an alternative version of Scala that is different from the Scala
version used in the current build (but hopefully binary compatible).

This is useful in cases, where the binary build of a dependency with
the exact Scala version isn't yet available but an otherwise binary
compatible build (maybe with a previous Scala release) is available.
This commit is contained in:
Indrajit Raychaudhuri 2011-11-16 14:04:16 +05:30
parent a49c907146
commit 0270ac078f
4 changed files with 17 additions and 16 deletions

View File

@ -4,7 +4,7 @@
package sbt package sbt
package impl package impl
import StringUtilities.{appendable,nonEmpty} import StringUtilities.nonEmpty
trait DependencyBuilders trait DependencyBuilders
{ {
@ -27,20 +27,21 @@ trait DependencyBuilders
final class GroupID private[sbt] (groupID: String) final class GroupID private[sbt] (groupID: String)
{ {
def % (artifactID: String) = groupArtifact(artifactID, false) def % (artifactID: String) = groupArtifact(artifactID, None)
def %% (artifactID: String) = groupArtifact(artifactID, true) def %% (artifactID: String, crossVersion: String => String = identity) = groupArtifact(artifactID, Some(crossVersion))
private def groupArtifact(artifactID: String, cross: Boolean) = def %% (artifactID: String, alternatives: (String, String)*) = groupArtifact(artifactID, Some(Map(alternatives: _*) orElse { case s => s }))
private def groupArtifact(artifactID: String, cross: Option[String => String]) =
{ {
nonEmpty(artifactID, "Artifact ID") nonEmpty(artifactID, "Artifact ID")
new GroupArtifactID(groupID, artifactID, cross) new GroupArtifactID(groupID, artifactID, cross)
} }
} }
final class GroupArtifactID private[sbt] (groupID: String, artifactID: String, crossVersion: Boolean) final class GroupArtifactID private[sbt] (groupID: String, artifactID: String, crossVersion: Option[String => String])
{ {
def % (revision: String): ModuleID = def % (revision: String): ModuleID =
{ {
nonEmpty(revision, "Revision") nonEmpty(revision, "Revision")
ModuleID(groupID, artifactID, revision).cross(crossVersion) ModuleID(groupID, artifactID, revision).cross(!crossVersion.isEmpty, crossVersion.getOrElse(identity))
} }
} }
final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID) final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID)

View File

@ -314,7 +314,7 @@ private object IvySbt
as.map(art => substituteCross(art, cross)) as.map(art => substituteCross(art, cross))
def substituteCross(m: ModuleID, cross: String): ModuleID = def substituteCross(m: ModuleID, cross: String): ModuleID =
if(m.crossVersion) if(m.crossVersion)
m.copy(name = crossName(m.name, cross), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross)) m.copy(name = crossName(m.name, m.crossVersionRemap(cross)), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross))
else else
m m

View File

@ -205,7 +205,7 @@ object IvyActions
report.allMissing flatMap { case (_, mod, art) => art.classifier.map { c => (restrictedCopy(mod, false), c) } } groupBy(_._1) map { case (mod, pairs) => (mod, pairs.map(_._2).toSet) } report.allMissing flatMap { case (_, mod, art) => art.classifier.map { c => (restrictedCopy(mod, false), c) } } groupBy(_._1) map { case (mod, pairs) => (mod, pairs.map(_._2).toSet) }
private[this] def restrictedCopy(m: ModuleID, confs: Boolean) = private[this] def restrictedCopy(m: ModuleID, confs: Boolean) =
ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion, extraAttributes = m.extraAttributes, configurations = if(confs) m.configurations else None) ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion, crossVersionRemap = m.crossVersionRemap, extraAttributes = m.extraAttributes, configurations = if(confs) m.configurations else None)
private[this] def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): (ResolveReport, Option[ResolveException]) = private[this] def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): (ResolveReport, Option[ResolveException]) =
{ {
val resolveOptions = new ResolveOptions val resolveOptions = new ResolveOptions
@ -268,4 +268,4 @@ object IvyActions
} }
} }
final class ResolveException(val messages: Seq[String], val failed: Seq[ModuleID]) extends RuntimeException(messages.mkString("\n")) final class ResolveException(val messages: Seq[String], val failed: Seq[ModuleID]) extends RuntimeException(messages.mkString("\n"))

View File

@ -9,14 +9,14 @@ import scala.xml.NodeSeq
import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver} import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver}
import org.apache.ivy.util.url.CredentialsStore import org.apache.ivy.util.url.CredentialsStore
final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String,String] = Map.empty, crossVersion: Boolean = false) final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String,String] = Map.empty, crossVersion: Boolean = false, crossVersionRemap: String => String = identity)
{ {
override def toString = override def toString =
organization + ":" + name + ":" + revision + organization + ":" + name + ":" + revision +
(configurations match { case Some(s) => ":" + s; case None => "" }) + (configurations match { case Some(s) => ":" + s; case None => "" }) +
(if(extraAttributes.isEmpty) "" else " " + extraString) (if(extraAttributes.isEmpty) "" else " " + extraString)
def extraString = extraAttributes.map { case (k,v) => k + "=" + v } mkString("(",", ",")") def extraString = extraAttributes.map { case (k,v) => k + "=" + v } mkString("(",", ",")")
def cross(v: Boolean) = copy(crossVersion = v) def cross(v: Boolean, verRemap: String => String = identity) = copy(crossVersion = v, crossVersionRemap = verRemap)
// () required for chaining // () required for chaining
def notTransitive() = intransitive() def notTransitive() = intransitive()
def intransitive() = copy(isTransitive = false) def intransitive() = copy(isTransitive = false)
@ -282,7 +282,7 @@ object Resolver
def resolveAll(patterns: Seq[String]) = patterns.map(p => resolvePattern(base, p)) def resolveAll(patterns: Seq[String]) = patterns.map(p => resolvePattern(base, p))
Patterns(resolveAll(basePatterns.ivyPatterns), resolveAll(basePatterns.artifactPatterns), basePatterns.isMavenCompatible) Patterns(resolveAll(basePatterns.ivyPatterns), resolveAll(basePatterns.artifactPatterns), basePatterns.isMavenCompatible)
} }
private[sbt] def resolvePattern(base: String, pattern: String): String = private[sbt] def resolvePattern(base: String, pattern: String): String =
{ {
val normBase = base.replace('\\', '/') val normBase = base.replace('\\', '/')
if(normBase.endsWith("/") || pattern.startsWith("/")) normBase + pattern else normBase + "/" + pattern if(normBase.endsWith("/") || pattern.startsWith("/")) normBase + pattern else normBase + "/" + pattern
@ -423,14 +423,14 @@ object Artifact
{ {
import artifact._ import artifact._
val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c } val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c }
val base = if(module.crossVersion) IvySbt.crossName(artifact.name, scalaVersion) else artifact.name val base = if(module.crossVersion) IvySbt.crossName(artifact.name, module.crossVersionRemap(scalaVersion)) else artifact.name
base + "-" + module.revision + classifierStr + "." + artifact.extension base + "-" + module.revision + classifierStr + "." + artifact.extension
} }
def cross(enable: Boolean, scalaVersion: String): String = if(enable) "_" + scalaVersion else "" def cross(enable: Boolean, scalaVersion: String): String = if(enable) "_" + scalaVersion else ""
val classifierConfMap = Map(SourceClassifier -> Sources, DocClassifier -> Docs) val classifierConfMap = Map(SourceClassifier -> Sources, DocClassifier -> Docs)
val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType)
def classifierConf(classifier: String): Configuration = classifierConfMap.getOrElse(classifier, Optional) def classifierConf(classifier: String): Configuration = classifierConfMap.getOrElse(classifier, Optional)
def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier, DefaultType) def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier, DefaultType)
def classified(name: String, classifier: String): Artifact = def classified(name: String, classifier: String): Artifact =
Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None) Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None)
@ -440,4 +440,4 @@ object ModuleConfiguration
{ {
def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver) def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver)
def apply(org: String, name: String, resolver: Resolver): ModuleConfiguration = ModuleConfiguration(org, name, "*", resolver) def apply(org: String, name: String, resolver: Resolver): ModuleConfiguration = ModuleConfiguration(org, name, "*", resolver)
} }