From 45e097e28148699910afa2b0c585ab38b201553e Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sun, 22 Jan 2012 22:06:52 -0500 Subject: [PATCH 1/2] org.scala-tools.sbt -> org.scala-sbt and dropping scala-tools.org from defaults --- ivy/ComponentManager.scala | 2 +- ivy/ConflictWarning.scala | 2 +- ivy/IvyInterface.scala | 3 +-- ivy/IvyScala.scala | 4 ++++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ivy/ComponentManager.scala b/ivy/ComponentManager.scala index 32979ab3b..de87a9890 100644 --- a/ivy/ComponentManager.scala +++ b/ivy/ComponentManager.scala @@ -61,7 +61,7 @@ class ComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentPr /** Retrieve the file for component 'id' from the local repository. */ private def update(id: String): Unit = ivyCache.withCachedJar(sbtModuleID(id), Some(globalLock), log)(jar => define(id, Seq(jar)) ) - private def sbtModuleID(id: String) = ModuleID("org.scala-tools.sbt", id, ComponentManager.stampedVersion) + private def sbtModuleID(id: String) = ModuleID(SbtArtifacts.Organization, id, ComponentManager.stampedVersion) /** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */ def cache(id: String): Unit = ivyCache.cacheJar(sbtModuleID(id), file(id)(IfMissing.Fail), Some(globalLock), log) def clearCache(id: String): Unit = lockGlobalCache { ivyCache.clearCachedJar(sbtModuleID(id), Some(globalLock), log) } diff --git a/ivy/ConflictWarning.scala b/ivy/ConflictWarning.scala index 738ab50fc..befe4abd6 100644 --- a/ivy/ConflictWarning.scala +++ b/ivy/ConflictWarning.scala @@ -5,7 +5,7 @@ package sbt final case class ConflictWarning(label: String, filter: ModuleFilter, group: ModuleID => String, level: Level.Value, failOnConflict: Boolean) object ConflictWarning { - def default(label: String): ConflictWarning = ConflictWarning(label, moduleFilter(organization = GlobFilter("org.scala-tools.sbt") | GlobFilter("org.scala-lang")), (_: ModuleID).organization, Level.Warn, false) + def default(label: String): ConflictWarning = ConflictWarning(label, moduleFilter(organization = GlobFilter(SbtArtifacts.Organization) | GlobFilter(ScalaArtifacts.Organization)), (_: ModuleID).organization, Level.Warn, false) def apply(config: ConflictWarning, report: UpdateReport, log: Logger) { diff --git a/ivy/IvyInterface.scala b/ivy/IvyInterface.scala index 7bf72b0dc..8d921c2ec 100644 --- a/ivy/IvyInterface.scala +++ b/ivy/IvyInterface.scala @@ -199,8 +199,7 @@ object Resolver def withDefaultResolvers(userResolvers: Seq[Resolver], mavenCentral: Boolean, scalaTools: Boolean): Seq[Resolver] = Seq(Resolver.defaultLocal) ++ userResolvers ++ - single(DefaultMavenRepository, mavenCentral)++ - single(ScalaToolsReleases, scalaTools) + single(DefaultMavenRepository, mavenCentral) private def single[T](value: T, nonEmpty: Boolean): Seq[T] = if(nonEmpty) Seq(value) else Nil /** A base class for defining factories for interfaces to Ivy repositories that require a hostname , port, and patterns. */ diff --git a/ivy/IvyScala.scala b/ivy/IvyScala.scala index 9926f7141..54f95ccf6 100644 --- a/ivy/IvyScala.scala +++ b/ivy/IvyScala.scala @@ -19,6 +19,10 @@ object ScalaArtifacts val CompilerID = "scala-compiler" def libraryDependency(version: String): ModuleID = ModuleID(Organization, LibraryID, version) } +object SbtArtifacts +{ + val Organization = "org.scala-sbt" +} import ScalaArtifacts._ From 80589d1f5ba039a683d04df0fbe4fb0e1f8a451b Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sun, 22 Jan 2012 22:06:53 -0500 Subject: [PATCH 2/2] rework cross versioning to account for prerelease Scala versions --- ivy/CrossVersion.scala | 94 ++++++++++++++++++++++++++++++++++++ ivy/DependencyBuilders.scala | 19 +++++--- ivy/Ivy.scala | 21 +++----- ivy/IvyActions.scala | 18 +++---- ivy/IvyInterface.scala | 17 +++++-- ivy/IvyScala.scala | 13 ++--- 6 files changed, 142 insertions(+), 40 deletions(-) create mode 100644 ivy/CrossVersion.scala diff --git a/ivy/CrossVersion.scala b/ivy/CrossVersion.scala new file mode 100644 index 000000000..59b51c795 --- /dev/null +++ b/ivy/CrossVersion.scala @@ -0,0 +1,94 @@ +package sbt + +final case class ScalaVersion(full: String, binary: String) + +sealed trait CrossVersion +object CrossVersion +{ + val TransitionScalaVersion = "2.10" + val TransitionSbtVersion = "0.12" + + object Disabled extends CrossVersion { override def toString = "disabled" } + final class Binary(val remapVersion: String => String) extends CrossVersion { + override def toString = "Binary" + } + final class Full(val remapVersion: String => String) extends CrossVersion { + override def toString = "Full" + } + + def full: CrossVersion = new Full(idFun) + def fullMapped(remapVersion: String => String): CrossVersion = new Full(remapVersion) + + def binary: CrossVersion = new Binary(idFun) + def binaryMapped(remapVersion: String => String): CrossVersion = new Full(remapVersion) + + private[this] def idFun[T]: T => T = x => x + def append(s: String): Option[String => String] = Some(x => crossName(x, s)) + + def apply(cross: CrossVersion, fullVersion: String, binaryVersion: String): Option[String => String] = + cross match + { + case Disabled => None + case b: Binary => append(b.remapVersion(binaryVersion)) + case f: Full => append(f.remapVersion(fullVersion)) + } + + def apply(module: ModuleID, is: IvyScala): Option[String => String] = + CrossVersion(module.crossVersion, is.scalaFullVersion, is.scalaBinaryVersion) + + def apply(module: ModuleID, is: Option[IvyScala]): Option[String => String] = + is flatMap { i => apply(module, i) } + + def substituteCross(artifacts: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] = + cross match { + case None => artifacts + case Some(is) => substituteCrossA(artifacts, cross) + } + + def applyCross(s: String, fopt: Option[String => String]): String = + fopt match { + case None => s + case Some(fopt) => fopt(s) + } + + def crossName(name: String, cross: String): String = + name + "_" + cross + def substituteCross(a: Artifact, cross: Option[String => String]): Artifact = + a.copy(name = applyCross(a.name, cross)) + def substituteCrossA(as: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] = + as.map(art => substituteCross(art, cross)) + + def apply(scalaFullVersion: String, scalaBinaryVersion: String): ModuleID => ModuleID = m => + { + val cross = apply(m.crossVersion, scalaFullVersion, scalaBinaryVersion) + if(cross.isDefined) + m.copy(name = applyCross(m.name, cross), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross)) + else + m + } + + def isStable(v: String): Boolean = !v.contains("-") + def selectVersion(full: String, binary: String): String = if(isStable(full)) binary else full + + val PartialVersion = """(\d+)\.(\d+)(?:\..+)?""".r + def partialVersion(s: String): Option[(Int,Int)] = + s match { + case PartialVersion(major, minor) => Some(major.toInt, minor.toInt) + case _ => None + } + private[this] def isNewer(major: Int, minor: Int, minMajor: Int, minMinor: Int): Boolean = + major > minMajor || (major == minMajor && minor >= minMinor) + + def binaryScalaVersion(full: String): String = binaryVersion(full, TransitionScalaVersion) + def binarySbtVersion(full: String): String = binaryVersion(full, TransitionSbtVersion) + def binaryVersion(full: String, cutoff: String): String = + { + def sub(major: Int, minor: Int) = major + "." + minor + (partialVersion(full), partialVersion(cutoff)) match { + case (Some((major, minor)), None) => sub(major, minor) + case (Some((major, minor)), Some((minMajor, minMinor))) if isNewer(major, minor, minMajor, minMinor) => sub(major, minor) + case _ => full + } + } +} + diff --git a/ivy/DependencyBuilders.scala b/ivy/DependencyBuilders.scala index b878f206c..7d5812aaf 100755 --- a/ivy/DependencyBuilders.scala +++ b/ivy/DependencyBuilders.scala @@ -27,21 +27,28 @@ trait DependencyBuilders final class GroupID private[sbt] (groupID: String) { - def % (artifactID: String) = groupArtifact(artifactID, None) - def %% (artifactID: String, crossVersion: String => String = identity) = groupArtifact(artifactID, Some(crossVersion)) - def %% (artifactID: String, alternatives: (String, String)*) = groupArtifact(artifactID, Some(Map(alternatives: _*) orElse { case s => s })) - private def groupArtifact(artifactID: String, cross: Option[String => String]) = + def % (artifactID: String) = groupArtifact(artifactID, CrossVersion.Disabled) + def %% (artifactID: String): GroupArtifactID = groupArtifact(artifactID, CrossVersion.binary) + + @deprecated(deprecationMessage, "0.12.0") + def %% (artifactID: String, crossVersion: String => String) = groupArtifact(artifactID, CrossVersion.binaryMapped(crossVersion)) + @deprecated(deprecationMessage, "0.12.0") + def %% (artifactID: String, alternatives: (String, String)*) = groupArtifact(artifactID, CrossVersion.binaryMapped(Map(alternatives: _*) orElse { case s => s })) + + private def groupArtifact(artifactID: String, cross: CrossVersion) = { nonEmpty(artifactID, "Artifact ID") new GroupArtifactID(groupID, artifactID, cross) } + + private[this] def deprecationMessage = """Use the cross method on the constructed ModuleID. For example: ("a" % "b" % "1").cross(...)""" } -final class GroupArtifactID private[sbt] (groupID: String, artifactID: String, crossVersion: Option[String => String]) +final class GroupArtifactID private[sbt] (groupID: String, artifactID: String, crossVersion: CrossVersion) { def % (revision: String): ModuleID = { nonEmpty(revision, "Revision") - ModuleID(groupID, artifactID, revision).cross(!crossVersion.isEmpty, crossVersion.getOrElse(identity)) + ModuleID(groupID, artifactID, revision).cross(crossVersion) } } final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID) diff --git a/ivy/Ivy.scala b/ivy/Ivy.scala index 19252fb28..400490086 100644 --- a/ivy/Ivy.scala +++ b/ivy/Ivy.scala @@ -299,24 +299,19 @@ private object IvySbt } private def substituteCross(m: ModuleSettings): ModuleSettings = - m.ivyScala match { case None => m; case Some(is) => substituteCross(m, is.substituteCross) } - private def substituteCross(m: ModuleSettings, sub: ModuleID => ModuleID): ModuleSettings = + m.ivyScala match { + case None => m + case Some(is) => substituteCross(m, is.scalaFullVersion, is.scalaBinaryVersion) + } + private def substituteCross(m: ModuleSettings, scalaFullVersion: String, scalaBinaryVersion: String): ModuleSettings = + { + val sub = CrossVersion(scalaFullVersion, scalaBinaryVersion) m match { case ec: EmptyConfiguration => ec.copy(module = sub(ec.module)) case ic: InlineConfiguration => ic.copy(module = sub(ic.module), dependencies = ic.dependencies map sub) case _ => m } - def crossName(name: String, cross: String): String = - name + "_" + cross - def substituteCross(a: Artifact, cross: String): Artifact = - a.copy(name = crossName(a.name, cross)) - def substituteCrossA(as: Seq[Artifact], cross: String): Seq[Artifact] = - as.map(art => substituteCross(art, cross)) - def substituteCross(m: ModuleID, cross: String): ModuleID = - if(m.crossVersion) - m.copy(name = crossName(m.name, m.crossVersionRemap(cross)), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross)) - else - m + } private def toIvyArtifact(moduleID: ModuleDescriptor, a: Artifact, configurations: Iterable[String]): MDArtifact = { diff --git a/ivy/IvyActions.scala b/ivy/IvyActions.scala index 1cabd0a31..059d48997 100644 --- a/ivy/IvyActions.scala +++ b/ivy/IvyActions.scala @@ -88,8 +88,8 @@ object IvyActions val resolver = ivy.getSettings.getResolver(resolverName) if(resolver eq null) error("Undefined resolver '" + resolverName + "'") val ivyArtifact = ivyFile map { file => (MDArtifact.newIvyArtifact(md), file) } - val is = crossIvyScala(module.moduleSettings) - val as = mapArtifacts(md, is, artifacts) ++ ivyArtifact.toList + val cross = crossVersionMap(module.moduleSettings) + val as = mapArtifacts(md, cross, artifacts) ++ ivyArtifact.toList withChecksums(resolver, checksums) { publish(md, as, resolver, overwrite = true) } } } @@ -102,18 +102,16 @@ object IvyActions try { act } finally { resolver.setChecksums(previous mkString ",") } } - private def crossIvyScala(moduleSettings: ModuleSettings): Option[IvyScala] = + private def crossVersionMap(moduleSettings: ModuleSettings): Option[String => String] = moduleSettings match { - case i: InlineConfiguration if i.module.crossVersion => i.ivyScala - case e: EmptyConfiguration if e.module.crossVersion => e.ivyScala + case i: InlineConfiguration => CrossVersion(i.module, i.ivyScala) + case e: EmptyConfiguration => CrossVersion(e.module, e.ivyScala) case _ => None } - def substituteCross(ivyScala: Option[IvyScala], artifacts: Seq[Artifact]): Seq[Artifact] = - ivyScala match { case None => artifacts; case Some(is) => IvySbt.substituteCrossA(artifacts, is.scalaVersion) } - def mapArtifacts(module: ModuleDescriptor, ivyScala: Option[IvyScala], artifacts: Map[Artifact, File]): Seq[(IArtifact, File)] = + def mapArtifacts(module: ModuleDescriptor, cross: Option[String => String], artifacts: Map[Artifact, File]): Seq[(IArtifact, File)] = { val rawa = artifacts.keys.toSeq - val seqa = substituteCross(ivyScala, rawa) + val seqa = CrossVersion.substituteCross(rawa, cross) val zipped = rawa zip IvySbt.mapArtifacts(module, seqa) zipped map { case (a, ivyA) => (ivyA, artifacts(a)) } } @@ -199,7 +197,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) } private[this] def restrictedCopy(m: ModuleID, confs: Boolean) = - ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion, crossVersionRemap = m.crossVersionRemap, extraAttributes = m.extraAttributes, configurations = if(confs) m.configurations else None) + ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion, 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]) = { val resolveOptions = new ResolveOptions diff --git a/ivy/IvyInterface.scala b/ivy/IvyInterface.scala index 8d921c2ec..d5ffc4d6a 100644 --- a/ivy/IvyInterface.scala +++ b/ivy/IvyInterface.scala @@ -9,14 +9,21 @@ import scala.xml.NodeSeq import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver} 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, crossVersionRemap: String => String = identity) +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: CrossVersion = CrossVersion.Disabled) { override def toString = organization + ":" + name + ":" + revision + (configurations match { case Some(s) => ":" + s; case None => "" }) + (if(extraAttributes.isEmpty) "" else " " + extraString) def extraString = extraAttributes.map { case (k,v) => k + "=" + v } mkString("(",", ",")") - def cross(v: Boolean, verRemap: String => String = identity) = copy(crossVersion = v, crossVersionRemap = verRemap) + + @deprecated("Use the variant accepting a CrossVersion value constructed by a member of the CrossVersion object.", "0.12.0") + def cross(v: Boolean): ModuleID = cross(if(v) CrossVersion.binary else CrossVersion.Disabled) + @deprecated("Use the variant accepting a CrossVersion value constructed by a member of the CrossVersion object.", "0.12.0") + def cross(v: Boolean, verRemap: String => String): ModuleID = cross(if(v) CrossVersion.binaryMapped(verRemap) else CrossVersion.Disabled) + + def cross(v: CrossVersion): ModuleID = copy(crossVersion = v) + // () required for chaining def notTransitive() = intransitive() def intransitive() = copy(isTransitive = false) @@ -418,14 +425,14 @@ object Artifact val base = if(i >= 0) name.substring(0, i) else name Artifact(base, extract(name, DefaultType), extract(name, DefaultExtension), None, Nil, Some(file.toURI.toURL)) } - def artifactName(scalaVersion: String, module: ModuleID, artifact: Artifact): String = + def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String = { import artifact._ val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c } - val base = if(module.crossVersion) IvySbt.crossName(artifact.name, module.crossVersionRemap(scalaVersion)) else artifact.name + val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary) + val base = CrossVersion.applyCross(artifact.name, cross) base + "-" + module.revision + classifierStr + "." + artifact.extension } - def cross(enable: Boolean, scalaVersion: String): String = if(enable) "_" + scalaVersion else "" val classifierConfMap = Map(SourceClassifier -> Sources, DocClassifier -> Docs) val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType) diff --git a/ivy/IvyScala.scala b/ivy/IvyScala.scala index 54f95ccf6..647df693f 100644 --- a/ivy/IvyScala.scala +++ b/ivy/IvyScala.scala @@ -26,7 +26,7 @@ object SbtArtifacts import ScalaArtifacts._ -final case class IvyScala(scalaVersion: String, configurations: Iterable[Configuration], checkExplicit: Boolean, filterImplicit: Boolean, overrideScalaVersion: Boolean, substituteCross: ModuleID => ModuleID) +final case class IvyScala(scalaFullVersion: String, scalaBinaryVersion: String, configurations: Iterable[Configuration], checkExplicit: Boolean, filterImplicit: Boolean, overrideScalaVersion: Boolean) private object IvyScala { @@ -34,11 +34,11 @@ private object IvyScala def checkModule(module: DefaultModuleDescriptor, conf: String)(check: IvyScala) { if(check.checkExplicit) - checkDependencies(module, check.scalaVersion, check.configurations) + checkDependencies(module, check.scalaBinaryVersion, check.configurations) if(check.filterImplicit) excludeScalaJars(module, check.configurations) if(check.overrideScalaVersion) - overrideScalaVersion(module, check.scalaVersion) + overrideScalaVersion(module, check.scalaFullVersion) } def overrideScalaVersion(module: DefaultModuleDescriptor, version: String) { @@ -54,14 +54,15 @@ private object IvyScala /** Checks the immediate dependencies of module for dependencies on scala jars and verifies that the version on the * dependencies matches scalaVersion. */ - private def checkDependencies(module: ModuleDescriptor, scalaVersion: String, configurations: Iterable[Configuration]) + private def checkDependencies(module: ModuleDescriptor, scalaBinaryVersion: String, configurations: Iterable[Configuration]) { val configSet = if(configurations.isEmpty) (c: String) => true else configurationSet(configurations) for(dep <- module.getDependencies.toList) { val id = dep.getDependencyRevisionId - if(id.getOrganisation == Organization && id.getRevision != scalaVersion && dep.getModuleConfigurations.exists(configSet)) - error("Version specified for dependency " + id + " differs from Scala version in project (" + scalaVersion + ").") + val depBinaryVersion = CrossVersion.binaryScalaVersion(id.getRevision) + if(id.getOrganisation == Organization && depBinaryVersion != scalaBinaryVersion && dep.getModuleConfigurations.exists(configSet)) + error("Binary version for dependency " + id + " (" + depBinaryVersion + ") differs from Scala binary version in project (" + scalaBinaryVersion + ").") } } private def configurationSet(configurations: Iterable[Configuration]) = configurations.map(_.toString).toSet