mirror of https://github.com/sbt/sbt.git
Merge pull request #409 from adpi2/sbt-plugins-maven-path
Try resolve sbt plugin from valid Maven pattern
This commit is contained in:
commit
67ee08e964
|
|
@ -27,10 +27,16 @@ import org.apache.ivy.plugins.resolver.{
|
||||||
import org.apache.ivy.plugins.repository.url.{ URLRepository => URLRepo }
|
import org.apache.ivy.plugins.repository.url.{ URLRepository => URLRepo }
|
||||||
import org.apache.ivy.plugins.repository.file.{ FileResource, FileRepository => FileRepo }
|
import org.apache.ivy.plugins.repository.file.{ FileResource, FileRepository => FileRepo }
|
||||||
import java.io.{ File, IOException }
|
import java.io.{ File, IOException }
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
import org.apache.ivy.util.{ ChecksumHelper, FileUtil, Message }
|
|
||||||
import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact }
|
import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact }
|
||||||
|
import org.apache.ivy.core.module.id.ModuleRevisionId
|
||||||
|
import org.apache.ivy.core.module.descriptor.DefaultArtifact
|
||||||
import org.apache.ivy.core.report.DownloadReport
|
import org.apache.ivy.core.report.DownloadReport
|
||||||
|
import org.apache.ivy.plugins.resolver.util.{ ResolvedResource, ResourceMDParser }
|
||||||
|
import org.apache.ivy.util.{ ChecksumHelper, FileUtil, Message }
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
import sbt.internal.librarymanagement.mavenint.PomExtraDependencyAttributes
|
||||||
import sbt.io.IO
|
import sbt.io.IO
|
||||||
import sbt.util.Logger
|
import sbt.util.Logger
|
||||||
import sbt.librarymanagement._
|
import sbt.librarymanagement._
|
||||||
|
|
@ -173,6 +179,32 @@ private[sbt] object ConvertResolver {
|
||||||
setArtifactPatterns(pattern)
|
setArtifactPatterns(pattern)
|
||||||
setIvyPatterns(pattern)
|
setIvyPatterns(pattern)
|
||||||
}
|
}
|
||||||
|
override protected def findResourceUsingPattern(
|
||||||
|
mrid: ModuleRevisionId,
|
||||||
|
pattern: String,
|
||||||
|
artifact: IArtifact,
|
||||||
|
rmdparser: ResourceMDParser,
|
||||||
|
date: Date
|
||||||
|
): ResolvedResource = {
|
||||||
|
val extraAttributes =
|
||||||
|
mrid.getExtraAttributes.asScala.toMap.asInstanceOf[Map[String, String]]
|
||||||
|
getSbtPluginCrossVersion(extraAttributes) match {
|
||||||
|
case Some(sbtCrossVersion) =>
|
||||||
|
// if the module is an sbt plugin
|
||||||
|
// we first try to resolve the artifact with the sbt cross version suffix
|
||||||
|
// and we fallback to the one without the suffix
|
||||||
|
val newArtifact = DefaultArtifact.cloneWithAnotherName(
|
||||||
|
artifact,
|
||||||
|
artifact.getName + sbtCrossVersion
|
||||||
|
)
|
||||||
|
val resolved =
|
||||||
|
super.findResourceUsingPattern(mrid, pattern, newArtifact, rmdparser, date)
|
||||||
|
if (resolved != null) resolved
|
||||||
|
else super.findResourceUsingPattern(mrid, pattern, artifact, rmdparser, date)
|
||||||
|
case None =>
|
||||||
|
super.findResourceUsingPattern(mrid, pattern, artifact, rmdparser, date)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val resolver = new PluginCapableResolver
|
val resolver = new PluginCapableResolver
|
||||||
if (repo.localIfFile) resolver.setRepository(new LocalIfFileRepo)
|
if (repo.localIfFile) resolver.setRepository(new LocalIfFileRepo)
|
||||||
|
|
@ -230,6 +262,13 @@ private[sbt] object ConvertResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def getSbtPluginCrossVersion(extraAttributes: Map[String, String]): Option[String] = {
|
||||||
|
for {
|
||||||
|
sbtVersion <- extraAttributes.get(PomExtraDependencyAttributes.SbtVersionKey)
|
||||||
|
scalaVersion <- extraAttributes.get(PomExtraDependencyAttributes.ScalaVersionKey)
|
||||||
|
} yield s"_${scalaVersion}_$sbtVersion"
|
||||||
|
}
|
||||||
|
|
||||||
private sealed trait DescriptorRequired extends BasicResolver {
|
private sealed trait DescriptorRequired extends BasicResolver {
|
||||||
// Works around implementation restriction to access protected method `get`
|
// Works around implementation restriction to access protected method `get`
|
||||||
def getResource(resource: Resource, dest: File): Long
|
def getResource(resource: Resource, dest: File): Long
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,31 @@ object CustomPomParser {
|
||||||
private[this] val unqualifiedKeys =
|
private[this] val unqualifiedKeys =
|
||||||
Set(SbtVersionKey, ScalaVersionKey, ExtraAttributesKey, ApiURLKey, VersionSchemeKey)
|
Set(SbtVersionKey, ScalaVersionKey, ExtraAttributesKey, ApiURLKey, VersionSchemeKey)
|
||||||
|
|
||||||
|
/** In the new POM format of sbt plugins, the dependency to an sbt plugin
|
||||||
|
* contains the sbt cross-version _2.12_1.0. The reason is we want Maven to be able
|
||||||
|
* to resolve the dependency using the pattern:
|
||||||
|
* <org>/<artifact-name>_2.12_1.0/<version>/<artifact-name>_2.12_1.0-<version>.pom
|
||||||
|
* In sbt 1.x we use extra-attributes to resolve sbt plugins, so here we must remove
|
||||||
|
* the sbt cross-version and keep the extra-attributes.
|
||||||
|
* Parsing a dependency found in the new POM format produces the same module as
|
||||||
|
* if it is found in the old POM format. It used not to contain the sbt cross-version
|
||||||
|
* suffix, but that was invalid.
|
||||||
|
* Hence we can resolve conflicts between new and old POM formats.
|
||||||
|
*
|
||||||
|
* To compare the two formats you can look at the POMs in:
|
||||||
|
* https://repo1.maven.org/maven2/ch/epfl/scala/sbt-plugin-example-diamond_2.12_1.0/0.5.0/
|
||||||
|
*/
|
||||||
|
private def removeSbtCrossVersion(
|
||||||
|
properties: Map[String, String],
|
||||||
|
moduleName: String
|
||||||
|
): String = {
|
||||||
|
val sbtCrossVersion = for {
|
||||||
|
sbtVersion <- properties.get(s"e:$SbtVersionKey")
|
||||||
|
scalaVersion <- properties.get(s"e:$ScalaVersionKey")
|
||||||
|
} yield s"_${scalaVersion}_$sbtVersion"
|
||||||
|
sbtCrossVersion.map(moduleName.stripSuffix).getOrElse(moduleName)
|
||||||
|
}
|
||||||
|
|
||||||
// packagings that should be jars, but that Ivy doesn't handle as jars
|
// packagings that should be jars, but that Ivy doesn't handle as jars
|
||||||
// TODO - move this elsewhere.
|
// TODO - move this elsewhere.
|
||||||
val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit", "scala-jar")
|
val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit", "scala-jar")
|
||||||
|
|
@ -163,9 +188,12 @@ object CustomPomParser {
|
||||||
import collection.JavaConverters._
|
import collection.JavaConverters._
|
||||||
val oldExtra = qualifiedExtra(id)
|
val oldExtra = qualifiedExtra(id)
|
||||||
val newExtra = (oldExtra ++ properties).asJava
|
val newExtra = (oldExtra ++ properties).asJava
|
||||||
|
// remove the sbt plugin cross version from the resolved ModuleRevisionId
|
||||||
|
// sbt-plugin-example_2.12_1.0 => sbt-plugin-example
|
||||||
|
val nameWithoutCrossVersion = removeSbtCrossVersion(properties, id.getName)
|
||||||
ModuleRevisionId.newInstance(
|
ModuleRevisionId.newInstance(
|
||||||
id.getOrganisation,
|
id.getOrganisation,
|
||||||
id.getName,
|
nameWithoutCrossVersion,
|
||||||
id.getBranch,
|
id.getBranch,
|
||||||
id.getRevision,
|
id.getRevision,
|
||||||
newExtra
|
newExtra
|
||||||
|
|
|
||||||
|
|
@ -219,9 +219,28 @@ final class IvySbt(
|
||||||
else IvySbt.cachedResolutionResolveCache.clean()
|
else IvySbt.cachedResolutionResolveCache.clean()
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Module(rawModuleSettings: ModuleSettings)
|
/**
|
||||||
|
* In the new POM format of sbt plugins, we append the sbt-cross version _2.12_1.0 to
|
||||||
|
* the module artifactId, and the artifactIds of its dependencies that are sbt plugins.
|
||||||
|
*
|
||||||
|
* The goal is to produce a valid Maven POM, a POM that Maven can resolve:
|
||||||
|
* Maven will try and succeed to resolve the POM of pattern:
|
||||||
|
* <org>/<artifact-name>_2.12_1.0/<version>/<artifact-name>_2.12_1.0-<version>.pom
|
||||||
|
*/
|
||||||
|
final class Module(rawModuleSettings: ModuleSettings, appendSbtCrossVersion: Boolean)
|
||||||
extends sbt.librarymanagement.ModuleDescriptor { self =>
|
extends sbt.librarymanagement.ModuleDescriptor { self =>
|
||||||
val moduleSettings: ModuleSettings = IvySbt.substituteCross(rawModuleSettings)
|
|
||||||
|
def this(rawModuleSettings: ModuleSettings) =
|
||||||
|
this(rawModuleSettings, appendSbtCrossVersion = false)
|
||||||
|
|
||||||
|
val moduleSettings: ModuleSettings =
|
||||||
|
rawModuleSettings match {
|
||||||
|
case ic: InlineConfiguration =>
|
||||||
|
val icWithCross = IvySbt.substituteCross(ic)
|
||||||
|
if (appendSbtCrossVersion) IvySbt.appendSbtCrossVersion(icWithCross)
|
||||||
|
else icWithCross
|
||||||
|
case m => m
|
||||||
|
}
|
||||||
|
|
||||||
def directDependencies: Vector[ModuleID] =
|
def directDependencies: Vector[ModuleID] =
|
||||||
moduleSettings match {
|
moduleSettings match {
|
||||||
|
|
@ -696,32 +715,44 @@ private[sbt] object IvySbt {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def substituteCross(m: ModuleSettings): ModuleSettings = {
|
private def substituteCross(ic: InlineConfiguration): InlineConfiguration = {
|
||||||
m.scalaModuleInfo match {
|
ic.scalaModuleInfo match {
|
||||||
case None => m
|
case None => ic
|
||||||
case Some(is) => substituteCross(m, is.scalaFullVersion, is.scalaBinaryVersion)
|
case Some(is) => substituteCross(ic, is.scalaFullVersion, is.scalaBinaryVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def substituteCross(
|
private def substituteCross(
|
||||||
m: ModuleSettings,
|
ic: InlineConfiguration,
|
||||||
scalaFullVersion: String,
|
scalaFullVersion: String,
|
||||||
scalaBinaryVersion: String
|
scalaBinaryVersion: String
|
||||||
): ModuleSettings = {
|
): InlineConfiguration = {
|
||||||
m match {
|
val applyCross = CrossVersion(scalaFullVersion, scalaBinaryVersion)
|
||||||
case ic: InlineConfiguration =>
|
def propagateCrossVersion(moduleID: ModuleID): ModuleID = {
|
||||||
val applyCross = CrossVersion(scalaFullVersion, scalaBinaryVersion)
|
val crossExclusions: Vector[ExclusionRule] =
|
||||||
def propagateCrossVersion(moduleID: ModuleID): ModuleID = {
|
moduleID.exclusions.map(CrossVersion.substituteCross(_, ic.scalaModuleInfo))
|
||||||
val crossExclusions: Vector[ExclusionRule] =
|
applyCross(moduleID)
|
||||||
moduleID.exclusions.map(CrossVersion.substituteCross(_, ic.scalaModuleInfo))
|
.withExclusions(crossExclusions)
|
||||||
applyCross(moduleID)
|
|
||||||
.withExclusions(crossExclusions)
|
|
||||||
}
|
|
||||||
ic.withModule(applyCross(ic.module))
|
|
||||||
.withDependencies(ic.dependencies.map(propagateCrossVersion))
|
|
||||||
.withOverrides(ic.overrides map applyCross)
|
|
||||||
case _ => m
|
|
||||||
}
|
}
|
||||||
|
ic.withModule(applyCross(ic.module))
|
||||||
|
.withDependencies(ic.dependencies.map(propagateCrossVersion))
|
||||||
|
.withOverrides(ic.overrides map applyCross)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def appendSbtCrossVersion(ic: InlineConfiguration): InlineConfiguration =
|
||||||
|
ic.withModule(appendSbtCrossVersion(ic.module))
|
||||||
|
.withDependencies(ic.dependencies.map(appendSbtCrossVersion))
|
||||||
|
.withOverrides(ic.overrides.map(appendSbtCrossVersion))
|
||||||
|
|
||||||
|
private def appendSbtCrossVersion(mid: ModuleID): ModuleID = {
|
||||||
|
val crossVersion = for {
|
||||||
|
scalaVersion <- mid.extraAttributes.get("e:scalaVersion")
|
||||||
|
sbtVersion <- mid.extraAttributes.get("e:sbtVersion")
|
||||||
|
} yield s"_${scalaVersion}_$sbtVersion"
|
||||||
|
crossVersion
|
||||||
|
.filter(!mid.name.endsWith(_))
|
||||||
|
.map(cv => mid.withName(mid.name + cv))
|
||||||
|
.getOrElse(mid)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def toIvyArtifact(
|
private def toIvyArtifact(
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,8 @@ trait BaseIvySpecification extends AbstractEngineSpec {
|
||||||
deps: Vector[ModuleID],
|
deps: Vector[ModuleID],
|
||||||
scalaFullVersion: Option[String],
|
scalaFullVersion: Option[String],
|
||||||
uo: UpdateOptions = UpdateOptions(),
|
uo: UpdateOptions = UpdateOptions(),
|
||||||
overrideScalaVersion: Boolean = true
|
overrideScalaVersion: Boolean = true,
|
||||||
|
appendSbtCrossVersion: Boolean = false
|
||||||
): IvySbt#Module = {
|
): IvySbt#Module = {
|
||||||
val scalaModuleInfo = scalaFullVersion map { fv =>
|
val scalaModuleInfo = scalaFullVersion map { fv =>
|
||||||
ScalaModuleInfo(
|
ScalaModuleInfo(
|
||||||
|
|
@ -55,7 +56,7 @@ trait BaseIvySpecification extends AbstractEngineSpec {
|
||||||
.withConfigurations(configurations)
|
.withConfigurations(configurations)
|
||||||
.withScalaModuleInfo(scalaModuleInfo)
|
.withScalaModuleInfo(scalaModuleInfo)
|
||||||
val ivySbt = new IvySbt(mkIvyConfiguration(uo))
|
val ivySbt = new IvySbt(mkIvyConfiguration(uo))
|
||||||
new ivySbt.Module(moduleSetting)
|
new ivySbt.Module(moduleSetting, appendSbtCrossVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
def resolvers: Vector[Resolver] = Vector(Resolver.mavenCentral)
|
def resolvers: Vector[Resolver] = Vector(Resolver.mavenCentral)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package sbt.internal.librarymanagement
|
||||||
|
|
||||||
|
import sbt.internal.librarymanagement.mavenint.PomExtraDependencyAttributes.{
|
||||||
|
SbtVersionKey,
|
||||||
|
ScalaVersionKey
|
||||||
|
}
|
||||||
|
import sbt.librarymanagement.{ CrossVersion, ModuleDescriptorConfiguration }
|
||||||
|
|
||||||
|
object IvyModuleSpec extends BaseIvySpecification {
|
||||||
|
|
||||||
|
test("The Scala binary version of a Scala module should be appended to its name") {
|
||||||
|
val m = module(
|
||||||
|
defaultModuleId.withCrossVersion(CrossVersion.Binary()),
|
||||||
|
Vector.empty,
|
||||||
|
Some("2.13.10")
|
||||||
|
)
|
||||||
|
m.moduleSettings match {
|
||||||
|
case configuration: ModuleDescriptorConfiguration =>
|
||||||
|
assert(configuration.module.name == "foo_2.13")
|
||||||
|
case _ => fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test("The sbt cross-version should be appended to the name of an sbt plugin") {
|
||||||
|
val m = module(
|
||||||
|
defaultModuleId.extra(SbtVersionKey -> "1.0", ScalaVersionKey -> "2.12"),
|
||||||
|
Vector.empty,
|
||||||
|
Some("2.12.17"),
|
||||||
|
appendSbtCrossVersion = true
|
||||||
|
)
|
||||||
|
m.moduleSettings match {
|
||||||
|
case configuration: ModuleDescriptorConfiguration =>
|
||||||
|
assert(configuration.module.name == "foo_2.12_1.0")
|
||||||
|
case _ => fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue