mirror of https://github.com/sbt/sbt.git
Configurable conflict manager and corresponding documentation on conflictManager, dependencyOverrides, and force(). Fixes #603.
This commit is contained in:
parent
b0a80efbda
commit
a9ac6b3983
|
|
@ -20,7 +20,6 @@ import core.module.descriptor.{OverrideDependencyDescriptorMediator}
|
|||
import core.module.id.{ArtifactId,ModuleId, ModuleRevisionId}
|
||||
import core.resolve.{IvyNode, ResolveData, ResolvedModuleRevision}
|
||||
import core.settings.IvySettings
|
||||
import plugins.conflict.{ConflictManager, LatestCompatibleConflictManager, LatestConflictManager}
|
||||
import plugins.latest.LatestRevisionStrategy
|
||||
import plugins.matcher.PatternMatcher
|
||||
import plugins.parser.m2.PomModuleDescriptorParser
|
||||
|
|
@ -140,6 +139,7 @@ final class IvySbt(val configuration: IvyConfiguration)
|
|||
{
|
||||
import ic._
|
||||
val moduleID = newConfiguredModuleID(module, moduleInfo, configurations)
|
||||
IvySbt.setConflictManager(moduleID, conflictManager, ivy.getSettings)
|
||||
val defaultConf = defaultConfiguration getOrElse Configurations.config(ModuleDescriptor.DEFAULT_CONFIGURATION)
|
||||
log.debug("Using inline dependencies specified in Scala" + (if(ivyXML.isEmpty) "." else " and XML."))
|
||||
|
||||
|
|
@ -359,6 +359,14 @@ private object IvySbt
|
|||
moduleID.setModuleArtifact(artifact)
|
||||
moduleID.check()
|
||||
}
|
||||
private def setConflictManager(moduleID: DefaultModuleDescriptor, conflict: ConflictManager, is: IvySettings)
|
||||
{
|
||||
val mid = ModuleId.newInstance(conflict.organization, conflict.module)
|
||||
val matcher = is.getMatcher(PatternMatcher.EXACT_OR_REGEXP)
|
||||
val manager = is.getConflictManager(conflict.name)
|
||||
moduleID.addConflictManager(mid, matcher, manager)
|
||||
}
|
||||
|
||||
/** Converts the given sbt module id into an Ivy ModuleRevisionId.*/
|
||||
def toID(m: ModuleID) =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ final case class PomConfiguration(file: File, ivyScala: Option[IvyScala], valida
|
|||
{
|
||||
def noScala = copy(ivyScala = None)
|
||||
}
|
||||
final case class InlineConfiguration(module: ModuleID, moduleInfo: ModuleInfo, dependencies: Seq[ModuleID], overrides: Set[ModuleID] = Set.empty, ivyXML: NodeSeq = NodeSeq.Empty, configurations: Seq[Configuration] = Nil, defaultConfiguration: Option[Configuration] = None, ivyScala: Option[IvyScala] = None, validate: Boolean = false) extends ModuleSettings
|
||||
final case class InlineConfiguration(module: ModuleID, moduleInfo: ModuleInfo, dependencies: Seq[ModuleID], overrides: Set[ModuleID] = Set.empty, ivyXML: NodeSeq = NodeSeq.Empty, configurations: Seq[Configuration] = Nil, defaultConfiguration: Option[Configuration] = None, ivyScala: Option[IvyScala] = None, validate: Boolean = false, conflictManager: ConflictManager = ConflictManager.default) extends ModuleSettings
|
||||
{
|
||||
def withConfigurations(configurations: Seq[Configuration]) = copy(configurations = configurations)
|
||||
def noScala = copy(ivyScala = None)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver}
|
|||
import org.apache.ivy.util.url.CredentialsStore
|
||||
|
||||
/** Additional information about a project module */
|
||||
case class ModuleInfo(nameFormal: String, description: String = "", homepage: Option[URL] = None, startYear: Option[Int] = None, licenses: Seq[(String, URL)] = Nil, organizationName: String = "", organizationHomepage: Option[URL] = None, scmInfo: Option[ScmInfo] = None)
|
||||
final case class ModuleInfo(nameFormal: String, description: String = "", homepage: Option[URL] = None, startYear: Option[Int] = None, licenses: Seq[(String, URL)] = Nil, organizationName: String = "", organizationHomepage: Option[URL] = None, scmInfo: Option[ScmInfo] = None)
|
||||
{
|
||||
def formally(name: String) = copy(nameFormal = name)
|
||||
def describing(desc: String, home: Option[URL]) = copy(description = desc, homepage = home)
|
||||
|
|
@ -19,10 +19,10 @@ case class ModuleInfo(nameFormal: String, description: String = "", homepage: Op
|
|||
}
|
||||
|
||||
/** Basic SCM information for a project module */
|
||||
case class ScmInfo(browseUrl: URL, connection: String, devConnection: Option[String] = None)
|
||||
final case class ScmInfo(browseUrl: URL, connection: String, devConnection: Option[String] = None)
|
||||
|
||||
/** Rule to exclude unwanted dependencies pulled in transitively by a module. */
|
||||
case class ExclusionRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil)
|
||||
final case class ExclusionRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil)
|
||||
|
||||
final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver)
|
||||
object ModuleConfiguration
|
||||
|
|
@ -30,3 +30,15 @@ object ModuleConfiguration
|
|||
def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver)
|
||||
def apply(org: String, name: String, resolver: Resolver): ModuleConfiguration = ModuleConfiguration(org, name, "*", resolver)
|
||||
}
|
||||
|
||||
final case class ConflictManager(name: String, organization: String = "*", module: String = "*")
|
||||
|
||||
/** See http://ant.apache.org/ivy/history/latest-milestone/settings/conflict-managers.html for details of the different conflict managers.*/
|
||||
object ConflictManager {
|
||||
val all = ConflictManager("all")
|
||||
val latestTime = ConflictManager("latest-time")
|
||||
val latestRevision = ConflictManager("latest-revision")
|
||||
val latestCompatible = ConflictManager("latest-compatible")
|
||||
val strict = ConflictManager("strict")
|
||||
val default = latestRevision
|
||||
}
|
||||
|
|
@ -119,6 +119,7 @@ object Defaults extends BuildCommon
|
|||
artifactClassifier in packageSrc :== Some(SourceClassifier),
|
||||
artifactClassifier in packageDoc :== Some(DocClassifier),
|
||||
checksums := Classpaths.bootChecksums(appConfiguration.value),
|
||||
conflictManager := ConflictManager.default,
|
||||
pomExtra :== NodeSeq.Empty,
|
||||
pomPostProcess :== idFun,
|
||||
pomAllRepositories :== false,
|
||||
|
|
@ -961,7 +962,7 @@ object Classpaths
|
|||
new IvySbt(conf)
|
||||
}
|
||||
def moduleSettings0: Initialize[Task[ModuleSettings]] = Def.task {
|
||||
new InlineConfiguration(projectID.value, projectInfo.value, allDependencies.value, dependencyOverrides.value, ivyXML.value, ivyConfigurations.value, defaultConfiguration.value, ivyScala.value, ivyValidate.value)
|
||||
new InlineConfiguration(projectID.value, projectInfo.value, allDependencies.value, dependencyOverrides.value, ivyXML.value, ivyConfigurations.value, defaultConfiguration.value, ivyScala.value, ivyValidate.value, conflictManager.value)
|
||||
}
|
||||
|
||||
def sbtClassifiersTasks = inTask(updateSbtClassifiers)(Seq(
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@ object Keys
|
|||
|
||||
val classifiersModule = TaskKey[GetClassifiersModule]("classifiers-module", rank = CTask)
|
||||
val conflictWarning = SettingKey[ConflictWarning]("conflict-warning", "Configures warnings for conflicts in dependency management.", CSetting)
|
||||
val conflictManager = SettingKey[ConflictManager]("conflict-manager", "Selects the conflict manager to use for dependency management.", CSetting)
|
||||
val autoScalaLibrary = SettingKey[Boolean]("auto-scala-library", "Adds a dependency on scala-library if true.", ASetting)
|
||||
val managedScalaInstance = SettingKey[Boolean]("managed-scala-instance", "Automatically obtains Scala tools as managed dependencies if true.", BSetting)
|
||||
val sbtResolver = SettingKey[Resolver]("sbt-resolver", "Provides a resolver for obtaining sbt as a dependency.", BMinusSetting)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
libraryDependencies ++= Seq(
|
||||
"org.spark-project" %% "spark-core" % "0.5.1",
|
||||
"log4j" % "log4j" % "1.2.17"
|
||||
)
|
||||
|
||||
scalaVersion := "2.9.2"
|
||||
|
||||
name := "conflict-manager-test"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
> update
|
||||
> set conflictManager := ConflictManager.strict
|
||||
-> update
|
||||
|
|
@ -399,6 +399,119 @@ The default value is:
|
|||
|
||||
checksums := Seq("sha1", "md5")
|
||||
|
||||
Conflict Management
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The conflict manager decides what to do when dependency resolution brings in different versions of the same library.
|
||||
By default, the latest revision is selected.
|
||||
This can be changed by setting ``conflictManager``, which has type `ConflictManager <../../api/sbt/ConflictManager.html>`_.
|
||||
See the `Ivy documentation <http://ant.apache.org/ivy/history/latest-milestone/settings/conflict-managers.html>`_ for details on the different conflict managers.
|
||||
For example, to specify that no conflicts are allowed,
|
||||
|
||||
::
|
||||
|
||||
conflictManager := ConflictManager.strict
|
||||
|
||||
With this set, any conflicts will generate an error.
|
||||
To resolve a conflict,
|
||||
|
||||
* configure a dependency override if the conflict is for a transitive dependency
|
||||
* force the revision if it is a direct dependency
|
||||
|
||||
Both are explained in the following sections.
|
||||
|
||||
Forcing a revision
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The following direct dependencies will introduce a conflict on the log4j version because spark requires log4j 1.2.16.
|
||||
|
||||
::
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"org.spark-project" %% "spark-core" % "0.5.1",
|
||||
"log4j" % "log4j" % "1.2.14"
|
||||
)
|
||||
|
||||
The default conflict manager will select the newer version of log4j, 1.2.16.
|
||||
This can be confirmed in the output of `show update`, which shows the newer version as being selected and the older version as not selected:
|
||||
|
||||
::
|
||||
|
||||
> show update
|
||||
[info] compile:
|
||||
[info] log4j:log4j:1.2.16: ...
|
||||
...
|
||||
[info] (EVICTED) log4j:log4j:1.2.14
|
||||
...
|
||||
|
||||
To say that we prefer the version we've specified over the version from indirect dependencies, use ``force()``:
|
||||
|
||||
::
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"org.spark-project" %% "spark-core" % "0.5.1",
|
||||
"log4j" % "log4j" % "1.2.14" force()
|
||||
)
|
||||
|
||||
The output of ``show update`` is now reversed:
|
||||
|
||||
::
|
||||
|
||||
> show update
|
||||
[info] compile:
|
||||
[info] log4j:log4j:1.2.14: ...
|
||||
...
|
||||
[info] (EVICTED) log4j:log4j:1.2.16
|
||||
...
|
||||
|
||||
**Note:** this is an Ivy-only feature and cannot be included in a published pom.xml.
|
||||
|
||||
|
||||
Forcing a revision without introducing a dependency
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Use of the ``force()`` method described in the previous section requires having a direct dependency.
|
||||
However, it may be desirable to force a revision without introducing that direct dependency.
|
||||
Ivy provides overrides for this and in sbt, overrides are configured in sbt with the ``dependencyOverrides`` setting, which is a set of ``ModuleID``s.
|
||||
For example, the following dependency definitions conflict because spark uses log4j 1.2.16 and scalaxb uses log4j 1.2.17:
|
||||
|
||||
::
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"org.spark-project" %% "spark-core" % "0.5.1",
|
||||
"org.scalaxb" %% "scalaxb" % "1.0.0"
|
||||
)
|
||||
|
||||
The default conflict manager chooses the latest revision of log4j, 1.2.17:
|
||||
|
||||
::
|
||||
|
||||
> show update
|
||||
[info] compile:
|
||||
[info] log4j:log4j:1.2.17: ...
|
||||
...
|
||||
[info] (EVICTED) log4j:log4j:1.2.16
|
||||
...
|
||||
|
||||
To change the version selected, add an override:
|
||||
|
||||
::
|
||||
|
||||
dependencyOverrides += "log4j" % "log4j" % "1.2.16"
|
||||
|
||||
This will not add a direct dependency on log4j, but will force the revision to be 1.2.16.
|
||||
This is confirmed by the output of ``show update``:
|
||||
|
||||
::
|
||||
|
||||
> show update
|
||||
[info] compile:
|
||||
[info] log4j:log4j:1.2.16
|
||||
...
|
||||
|
||||
**Note:** this is an Ivy-only feature and will not be included in a published pom.xml.
|
||||
|
||||
|
||||
.. _packaging-pom:
|
||||
|
||||
packaging="pom"
|
||||
|
|
|
|||
Loading…
Reference in New Issue