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.file.{ FileResource, FileRepository => FileRepo }
|
||||
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.id.ModuleRevisionId
|
||||
import org.apache.ivy.core.module.descriptor.DefaultArtifact
|
||||
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.util.Logger
|
||||
import sbt.librarymanagement._
|
||||
|
|
@ -173,6 +179,32 @@ private[sbt] object ConvertResolver {
|
|||
setArtifactPatterns(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
|
||||
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 {
|
||||
// Works around implementation restriction to access protected method `get`
|
||||
def getResource(resource: Resource, dest: File): Long
|
||||
|
|
|
|||
|
|
@ -75,6 +75,31 @@ object CustomPomParser {
|
|||
private[this] val unqualifiedKeys =
|
||||
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
|
||||
// TODO - move this elsewhere.
|
||||
val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit", "scala-jar")
|
||||
|
|
@ -163,9 +188,12 @@ object CustomPomParser {
|
|||
import collection.JavaConverters._
|
||||
val oldExtra = qualifiedExtra(id)
|
||||
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(
|
||||
id.getOrganisation,
|
||||
id.getName,
|
||||
nameWithoutCrossVersion,
|
||||
id.getBranch,
|
||||
id.getRevision,
|
||||
newExtra
|
||||
|
|
|
|||
|
|
@ -219,9 +219,28 @@ final class IvySbt(
|
|||
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 =>
|
||||
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] =
|
||||
moduleSettings match {
|
||||
|
|
@ -696,32 +715,44 @@ private[sbt] object IvySbt {
|
|||
)
|
||||
}
|
||||
|
||||
private def substituteCross(m: ModuleSettings): ModuleSettings = {
|
||||
m.scalaModuleInfo match {
|
||||
case None => m
|
||||
case Some(is) => substituteCross(m, is.scalaFullVersion, is.scalaBinaryVersion)
|
||||
private def substituteCross(ic: InlineConfiguration): InlineConfiguration = {
|
||||
ic.scalaModuleInfo match {
|
||||
case None => ic
|
||||
case Some(is) => substituteCross(ic, is.scalaFullVersion, is.scalaBinaryVersion)
|
||||
}
|
||||
}
|
||||
|
||||
private def substituteCross(
|
||||
m: ModuleSettings,
|
||||
ic: InlineConfiguration,
|
||||
scalaFullVersion: String,
|
||||
scalaBinaryVersion: String
|
||||
): ModuleSettings = {
|
||||
m match {
|
||||
case ic: InlineConfiguration =>
|
||||
val applyCross = CrossVersion(scalaFullVersion, scalaBinaryVersion)
|
||||
def propagateCrossVersion(moduleID: ModuleID): ModuleID = {
|
||||
val crossExclusions: Vector[ExclusionRule] =
|
||||
moduleID.exclusions.map(CrossVersion.substituteCross(_, ic.scalaModuleInfo))
|
||||
applyCross(moduleID)
|
||||
.withExclusions(crossExclusions)
|
||||
}
|
||||
ic.withModule(applyCross(ic.module))
|
||||
.withDependencies(ic.dependencies.map(propagateCrossVersion))
|
||||
.withOverrides(ic.overrides map applyCross)
|
||||
case _ => m
|
||||
): InlineConfiguration = {
|
||||
val applyCross = CrossVersion(scalaFullVersion, scalaBinaryVersion)
|
||||
def propagateCrossVersion(moduleID: ModuleID): ModuleID = {
|
||||
val crossExclusions: Vector[ExclusionRule] =
|
||||
moduleID.exclusions.map(CrossVersion.substituteCross(_, ic.scalaModuleInfo))
|
||||
applyCross(moduleID)
|
||||
.withExclusions(crossExclusions)
|
||||
}
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ trait BaseIvySpecification extends AbstractEngineSpec {
|
|||
deps: Vector[ModuleID],
|
||||
scalaFullVersion: Option[String],
|
||||
uo: UpdateOptions = UpdateOptions(),
|
||||
overrideScalaVersion: Boolean = true
|
||||
overrideScalaVersion: Boolean = true,
|
||||
appendSbtCrossVersion: Boolean = false
|
||||
): IvySbt#Module = {
|
||||
val scalaModuleInfo = scalaFullVersion map { fv =>
|
||||
ScalaModuleInfo(
|
||||
|
|
@ -55,7 +56,7 @@ trait BaseIvySpecification extends AbstractEngineSpec {
|
|||
.withConfigurations(configurations)
|
||||
.withScalaModuleInfo(scalaModuleInfo)
|
||||
val ivySbt = new IvySbt(mkIvyConfiguration(uo))
|
||||
new ivySbt.Module(moduleSetting)
|
||||
new ivySbt.Module(moduleSetting, appendSbtCrossVersion)
|
||||
}
|
||||
|
||||
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