Apply scalaVersion enforcement to Compile related configs

Fixes #2786. Ref #2634.

sbt 0.13.12 added Ivy mediator that enforces scalaOrganization and
scalaVersion for Scala toolchain artifacts.
This turns out to be a bit too aggressive because Ivy configurations
can be used as an independent dependency graph that does not rely on
the scalaVersion used by Compile configuration. By enforcing
scalaVersion in those graph causes runtime failure.

This change checks if the configuration extends Default, Compile,
Provided, or Optional before enforcing scalaVersion.
This commit is contained in:
Eugene Yokota 2016-11-11 02:44:56 -05:00
parent 55e2b56af9
commit 8d0e4d2ef0
6 changed files with 46 additions and 9 deletions

View File

@ -51,6 +51,15 @@ object Configurations {
private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration
private[sbt] def defaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) Configurations.Compile else Configurations.Default private[sbt] def defaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) Configurations.Compile else Configurations.Default
private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*) private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*)
/** Returns true if the configuration should be under the influence of scalaVersion. */
private[sbt] def underScalaVersion(c: Configuration): Boolean =
c match {
case Default | Compile | IntegrationTest | Provided | Runtime | Test | Optional |
CompilerPlugin | CompileInternal | RuntimeInternal | TestInternal => true
case config =>
config.extendsConfigs exists underScalaVersion
}
} }
/** Represents an Ivy configuration. */ /** Represents an Ivy configuration. */
final case class Configuration(name: String, description: String, isPublic: Boolean, extendsConfigs: List[Configuration], transitive: Boolean) { final case class Configuration(name: String, description: String, isPublic: Boolean, extendsConfigs: List[Configuration], transitive: Boolean) {

View File

@ -174,7 +174,21 @@ final class IvySbt(val configuration: IvyConfiguration) {
case pc: PomConfiguration => configurePom(pc) case pc: PomConfiguration => configurePom(pc)
case ifc: IvyFileConfiguration => configureIvyFile(ifc) case ifc: IvyFileConfiguration => configureIvyFile(ifc)
} }
moduleSettings.ivyScala.foreach(IvyScala.checkModule(baseModule, baseConfiguration, configuration.log))
val configs =
moduleSettings match {
case ic: InlineConfiguration => ic.configurations
case ic: InlineConfigurationWithExcludes => ic.configurations
case ec: EmptyConfiguration => Nil
case pc: PomConfiguration => Configurations.default ++ Configurations.defaultInternal
case ifc: IvyFileConfiguration => Configurations.default ++ Configurations.defaultInternal
}
moduleSettings.ivyScala match {
case Some(is) =>
val svc = configs.toVector filter Configurations.underScalaVersion map { _.name }
IvyScala.checkModule(baseModule, baseConfiguration, svc, configuration.log)(is)
case _ => // do nothing
}
IvySbt.addExtraNamespace(baseModule) IvySbt.addExtraNamespace(baseModule)
(baseModule, baseConfiguration) (baseModule, baseConfiguration)
} }

View File

@ -49,24 +49,28 @@ final case class IvyScala(scalaFullVersion: String, scalaBinaryVersion: String,
private object IvyScala { private object IvyScala {
/** Performs checks/adds filters on Scala dependencies (if enabled in IvyScala). */ /** Performs checks/adds filters on Scala dependencies (if enabled in IvyScala). */
def checkModule(module: DefaultModuleDescriptor, conf: String, log: Logger)(check: IvyScala): Unit = { def checkModule(module: DefaultModuleDescriptor, conf: String, scalaVersionConfigs: Vector[String], log: Logger)(check: IvyScala): Unit = {
if (check.checkExplicit) if (check.checkExplicit)
checkDependencies(module, check.scalaOrganization, check.scalaBinaryVersion, check.configurations, log) checkDependencies(module, check.scalaOrganization, check.scalaBinaryVersion, check.configurations, log)
if (check.filterImplicit) if (check.filterImplicit)
excludeScalaJars(module, check.configurations) excludeScalaJars(module, check.configurations)
if (check.overrideScalaVersion) if (check.overrideScalaVersion)
overrideScalaVersion(module, check.scalaOrganization, check.scalaFullVersion) overrideScalaVersion(module, check.scalaOrganization, check.scalaFullVersion, scalaVersionConfigs)
} }
class OverrideScalaMediator(scalaOrganization: String, scalaVersion: String) extends DependencyDescriptorMediator { class OverrideScalaMediator(scalaOrganization: String, scalaVersion: String, scalaVersionConfigs0: Vector[String]) extends DependencyDescriptorMediator {
private[this] val scalaVersionConfigs = scalaVersionConfigs0.toSet
def mediate(dd: DependencyDescriptor): DependencyDescriptor = { def mediate(dd: DependencyDescriptor): DependencyDescriptor = {
// Mediate only for the dependencies in scalaVersion configurations. https://github.com/sbt/sbt/issues/2786
def configQualifies: Boolean =
(dd.getModuleConfigurations exists { scalaVersionConfigs })
val transformer = val transformer =
new NamespaceTransformer { new NamespaceTransformer {
def transform(mrid: ModuleRevisionId): ModuleRevisionId = { def transform(mrid: ModuleRevisionId): ModuleRevisionId = {
if (mrid == null) mrid if (mrid == null) mrid
else else
mrid.getName match { mrid.getName match {
case name @ (CompilerID | LibraryID | ReflectID | ActorsID | ScalapID) => case name @ (CompilerID | LibraryID | ReflectID | ActorsID | ScalapID) if configQualifies =>
ModuleRevisionId.newInstance(scalaOrganization, name, mrid.getBranch, scalaVersion, mrid.getQualifiedExtraAttributes) ModuleRevisionId.newInstance(scalaOrganization, name, mrid.getBranch, scalaVersion, mrid.getQualifiedExtraAttributes)
case _ => mrid case _ => mrid
} }
@ -79,8 +83,8 @@ private object IvyScala {
} }
} }
def overrideScalaVersion(module: DefaultModuleDescriptor, organization: String, version: String): Unit = { def overrideScalaVersion(module: DefaultModuleDescriptor, organization: String, version: String, scalaVersionConfigs: Vector[String]): Unit = {
val mediator = new OverrideScalaMediator(organization, version) val mediator = new OverrideScalaMediator(organization, version, scalaVersionConfigs)
module.addDependencyDescriptorMediator(new ModuleId(Organization, "*"), ExactPatternMatcher.INSTANCE, mediator) module.addDependencyDescriptorMediator(new ModuleId(Organization, "*"), ExactPatternMatcher.INSTANCE, mediator)
if (organization != Organization) if (organization != Organization)
module.addDependencyDescriptorMediator(new ModuleId(organization, "*"), ExactPatternMatcher.INSTANCE, mediator) module.addDependencyDescriptorMediator(new ModuleId(organization, "*"), ExactPatternMatcher.INSTANCE, mediator)

View File

@ -14,10 +14,12 @@ object ScalaOverrideTest extends Specification {
val OtherOrgID = "other.org" val OtherOrgID = "other.org"
def check(org0: String, version0: String)(org1: String, name1: String, version1: String) = { def check(org0: String, version0: String)(org1: String, name1: String, version1: String) = {
val osm = new OverrideScalaMediator(org0, version0) val scalaConfigs = Configurations.default.toVector filter { Configurations.underScalaVersion } map { _.name }
val osm = new OverrideScalaMediator(org0, version0, scalaConfigs)
val mrid = ModuleRevisionId.newInstance(org1, name1, version1) val mrid = ModuleRevisionId.newInstance(org1, name1, version1)
val dd = new DefaultDependencyDescriptor(mrid, false) val dd = new DefaultDependencyDescriptor(mrid, false)
dd.addDependencyConfiguration("compile", "compile")
val res = osm.mediate(dd) val res = osm.mediate(dd)
res.getDependencyRevisionId must_== ModuleRevisionId.newInstance(org0, name1, version0) res.getDependencyRevisionId must_== ModuleRevisionId.newInstance(org0, name1, version0)

View File

@ -0,0 +1,9 @@
### Bug fixes
- Fixes a regression in sbt 0.13.12 that was misfiring Scala version enforcement when configuration does not extend `Compile`. [#2827][2827]/[#2786][2786] by [@eed3si9n][@eed3si9n]
[2786]: https://github.com/sbt/sbt/issues/2786
[2827]: https://github.com/sbt/sbt/pull/2827
[@eed3si9n]: https://github.com/eed3si9n
[@dwijnand]: https://github.com/dwijnand
[@Duhemm]: https://github.com/Duhemm

View File

@ -49,6 +49,5 @@ checkDependencies := {
m <- c.modules m <- c.modules
if !m.evicted if !m.evicted
} yield m.module.copy(extraAttributes = Map.empty)).toSet } yield m.module.copy(extraAttributes = Map.empty)).toSet
assert(resolved == expected) assert(resolved == expected)
} }