diff --git a/modules/lm-coursier/src/main/contraband/module.json b/modules/lm-coursier/src/main/contraband/module.json index 75907e000..a53095667 100644 --- a/modules/lm-coursier/src/main/contraband/module.json +++ b/modules/lm-coursier/src/main/contraband/module.json @@ -38,11 +38,18 @@ "name": "include", "type": "Set[lmcoursier.definitions.Module]", "doc": "Use \"*\" in either organization or name to match any." + }, + { + "name": "includeByDefault", + "type": "Boolean", + "default": "true", + "since": "2.0.0-RC4" } ], "extraCompanion": [ "/** ModuleMatchers that matches to any modules. */", - "def all: ModuleMatchers = ModuleMatchers(Set.empty, Set.empty)" + "def all: ModuleMatchers = ModuleMatchers(Set.empty, Set.empty)", + "def only(mod: Module): ModuleMatchers = ModuleMatchers(Set.empty, Set(mod), includeByDefault = false)" ] } ] diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ModuleMatchers.scala b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ModuleMatchers.scala index ff1646c42..2cdf51de8 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ModuleMatchers.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ModuleMatchers.scala @@ -10,22 +10,23 @@ package lmcoursier.definitions */ final class ModuleMatchers private ( val exclude: Set[lmcoursier.definitions.Module], - val include: Set[lmcoursier.definitions.Module]) extends Serializable { - + val include: Set[lmcoursier.definitions.Module], + val includeByDefault: Boolean) extends Serializable { + private def this(exclude: Set[lmcoursier.definitions.Module], include: Set[lmcoursier.definitions.Module]) = this(exclude, include, true) override def equals(o: Any): Boolean = o match { - case x: ModuleMatchers => (this.exclude == x.exclude) && (this.include == x.include) + case x: ModuleMatchers => (this.exclude == x.exclude) && (this.include == x.include) && (this.includeByDefault == x.includeByDefault) case _ => false } override def hashCode: Int = { - 37 * (37 * (37 * (17 + "lmcoursier.definitions.ModuleMatchers".##) + exclude.##) + include.##) + 37 * (37 * (37 * (37 * (17 + "lmcoursier.definitions.ModuleMatchers".##) + exclude.##) + include.##) + includeByDefault.##) } override def toString: String = { - "ModuleMatchers(" + exclude + ", " + include + ")" + "ModuleMatchers(" + exclude + ", " + include + ", " + includeByDefault + ")" } - private[this] def copy(exclude: Set[lmcoursier.definitions.Module] = exclude, include: Set[lmcoursier.definitions.Module] = include): ModuleMatchers = { - new ModuleMatchers(exclude, include) + private[this] def copy(exclude: Set[lmcoursier.definitions.Module] = exclude, include: Set[lmcoursier.definitions.Module] = include, includeByDefault: Boolean = includeByDefault): ModuleMatchers = { + new ModuleMatchers(exclude, include, includeByDefault) } def withExclude(exclude: Set[lmcoursier.definitions.Module]): ModuleMatchers = { copy(exclude = exclude) @@ -33,9 +34,14 @@ final class ModuleMatchers private ( def withInclude(include: Set[lmcoursier.definitions.Module]): ModuleMatchers = { copy(include = include) } + def withIncludeByDefault(includeByDefault: Boolean): ModuleMatchers = { + copy(includeByDefault = includeByDefault) + } } object ModuleMatchers { /** ModuleMatchers that matches to any modules. */ def all: ModuleMatchers = ModuleMatchers(Set.empty, Set.empty) + def only(mod: Module): ModuleMatchers = ModuleMatchers(Set.empty, Set(mod), includeByDefault = false) def apply(exclude: Set[lmcoursier.definitions.Module], include: Set[lmcoursier.definitions.Module]): ModuleMatchers = new ModuleMatchers(exclude, include) + def apply(exclude: Set[lmcoursier.definitions.Module], include: Set[lmcoursier.definitions.Module], includeByDefault: Boolean): ModuleMatchers = new ModuleMatchers(exclude, include, includeByDefault) } diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/definitions/Reconciliation.scala b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/Reconciliation.scala index 9af154894..b44d8335b 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/definitions/Reconciliation.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/Reconciliation.scala @@ -1,13 +1,15 @@ -/** - * This code is generated using [[http://www.scala-sbt.org/contraband/ sbt-contraband]]. - */ - -// DO NOT EDIT MANUALLY package lmcoursier.definitions sealed abstract class Reconciliation extends Serializable object Reconciliation { - - case object Default extends Reconciliation case object Relaxed extends Reconciliation + case object Strict extends Reconciliation + + def apply(input: String): Option[Reconciliation] = + input match { + case "default" => Some(Default) + case "relaxed" => Some(Relaxed) + case "strict" => Some(Strict) + case _ => None + } } diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala index e2296a575..abae8f2d8 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala @@ -49,6 +49,7 @@ object ToCoursier { r match { case Reconciliation.Default => coursier.core.Reconciliation.Default case Reconciliation.Relaxed => coursier.core.Reconciliation.Relaxed + case Reconciliation.Strict => coursier.core.Reconciliation.Strict } def reconciliation(rs: Vector[(ModuleMatchers, Reconciliation)]): diff --git a/modules/sbt-coursier-shared/src/main/scala/coursier/sbtcoursiershared/SbtCoursierShared.scala b/modules/sbt-coursier-shared/src/main/scala/coursier/sbtcoursiershared/SbtCoursierShared.scala index be2de497a..544bca381 100644 --- a/modules/sbt-coursier-shared/src/main/scala/coursier/sbtcoursiershared/SbtCoursierShared.scala +++ b/modules/sbt-coursier-shared/src/main/scala/coursier/sbtcoursiershared/SbtCoursierShared.scala @@ -9,7 +9,8 @@ import lmcoursier.definitions.{CacheLogger, Configuration, Project, Publication} import lmcoursier.internal.SbtCoursierCache import sbt.{AutoPlugin, Classpaths, Compile, Setting, TaskKey, Test, settingKey, taskKey} import sbt.Keys._ -import sbt.librarymanagement.{Resolver, URLRepository} +import sbt.librarymanagement.DependencyBuilders.OrganizationArtifactName +import sbt.librarymanagement.{ModuleID, Resolver, URLRepository} object SbtCoursierShared extends AutoPlugin { @@ -35,6 +36,7 @@ object SbtCoursierShared extends AutoPlugin { val coursierFallbackDependencies = taskKey[Seq[FallbackDependency]]("") val mavenProfiles = settingKey[Set[String]]("") + val versionReconciliation = taskKey[Seq[ModuleID]]("") val coursierUseSbtCredentials = settingKey[Boolean]("") @deprecated("Use coursierExtraCredentials rather than coursierCredentials", "1.1.0-M14") @@ -166,7 +168,8 @@ object SbtCoursierShared extends AutoPlugin { confs ++ extraSources.toSeq ++ extraDocs.toSeq }, - mavenProfiles := Set.empty + mavenProfiles := Set.empty, + versionReconciliation := Seq.empty ) ++ { if (pubSettings) IvyXml.generateIvyXmlSettings() diff --git a/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/ResolutionTasks.scala b/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/ResolutionTasks.scala index b6af7f239..f20518f2e 100644 --- a/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/ResolutionTasks.scala +++ b/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/ResolutionTasks.scala @@ -10,6 +10,7 @@ import lmcoursier.internal.{InterProjectRepository, ResolutionParams, Resolution import coursier.sbtcoursier.Keys._ import coursier.sbtcoursiershared.InputsTasks.{credentialsTask, strictTask} import coursier.sbtcoursiershared.SbtCoursierShared.autoImport._ +import coursier.util.{ModuleMatcher, ModuleMatchers} import sbt.Def import sbt.Keys._ @@ -75,6 +76,16 @@ object ResolutionTasks { val verbosityLevel = coursierVerbosity.value val userEnabledProfiles = mavenProfiles.value + val versionReconciliations0 = versionReconciliation.value.map { mod => + Reconciliation(mod.revision) match { + case Some(rec) => + val (mod0, _) = FromSbt.moduleVersion(mod, sv, sbv) + val matcher = ModuleMatchers.only(Organization(mod0.organization.value), ModuleName(mod0.name.value)) + matcher -> rec + case None => + throw new Exception(s"Unrecognized reconciliation: '${mod.revision}'") + } + } val typelevel = Organization(scalaOrganization.value) == Typelevel.typelevelOrg @@ -141,7 +152,8 @@ object ResolutionTasks { .withMaxIterations(maxIterations) .withProfiles(userEnabledProfiles) .withForceVersion(userForceVersions.map { case (k, v) => (ToCoursier.module(k), v) }.toMap) - .withTypelevel(typelevel), + .withTypelevel(typelevel) + .addReconciliation(versionReconciliations0: _*), strictOpt = strictOpt ), verbosityLevel, diff --git a/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/build.sbt b/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/build.sbt new file mode 100644 index 000000000..efdcfa4d7 --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/build.sbt @@ -0,0 +1,19 @@ + +lazy val shared = Seq( + scalaVersion := "2.12.8", + libraryDependencies ++= Seq( + "com.github.alexarchambault" %% "argonaut-shapeless_6.2" % "1.2.0-M4", + "com.chuusai" %% "shapeless" % "2.3.3" + ), + versionReconciliation += "*" % "*" % "strict" +) + +lazy val a = project + .settings(shared) + +lazy val b = project + .settings(shared) + .settings( + // strict cm should be fine if we force the conflicting module version + dependencyOverrides += "com.chuusai" %% "shapeless" % "2.3.3" + ) diff --git a/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/project/plugins.sbt b/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/project/plugins.sbt new file mode 100644 index 000000000..71a44ffd3 --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/project/plugins.sbt @@ -0,0 +1,13 @@ +addSbtPlugin { + + val name = sys.props.getOrElse( + "plugin.name", + sys.error("plugin.name Java property not set") + ) + val version = sys.props.getOrElse( + "plugin.version", + sys.error("plugin.version Java property not set") + ) + + "io.get-coursier" % name % version +} \ No newline at end of file diff --git a/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/test b/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/test new file mode 100644 index 000000000..0de45305f --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/shared-2/version-reconciliation/test @@ -0,0 +1,2 @@ +-> a/update +> b/update diff --git a/modules/sbt-lm-coursier/src/main/scala/coursier/sbtlmcoursier/LmCoursierPlugin.scala b/modules/sbt-lm-coursier/src/main/scala/coursier/sbtlmcoursier/LmCoursierPlugin.scala index ddc3a9021..b6bac8e3e 100644 --- a/modules/sbt-lm-coursier/src/main/scala/coursier/sbtlmcoursier/LmCoursierPlugin.scala +++ b/modules/sbt-lm-coursier/src/main/scala/coursier/sbtlmcoursier/LmCoursierPlugin.scala @@ -1,6 +1,6 @@ package coursier.sbtlmcoursier -import lmcoursier.definitions.Authentication +import lmcoursier.definitions.{Authentication, ModuleMatchers, Reconciliation} import lmcoursier.{CoursierConfiguration, CoursierDependencyResolution, Inputs} import coursier.sbtcoursiershared.InputsTasks.{credentialsTask, strictTask} import coursier.sbtcoursiershared.{InputsTasks, SbtCoursierShared} @@ -95,6 +95,16 @@ object LmCoursierPlugin extends AutoPlugin { val fallbackDeps = coursierFallbackDependencies.value val autoScalaLib = autoScalaLibrary.value && scalaModuleInfo.value.forall(_.overrideScalaVersion) val profiles = mavenProfiles.value + val versionReconciliations0 = versionReconciliation.value.map { mod => + Reconciliation(mod.revision) match { + case Some(rec) => + val (mod0, _) = lmcoursier.FromSbt.moduleVersion(mod, scalaVer, sbv) + val matcher = ModuleMatchers.only(mod0) + matcher -> rec + case None => + throw new Exception(s"Unrecognized reconciliation: '${mod.revision}'") + } + } val userForceVersions = Inputs.forceVersions(dependencyOverrides.value, scalaVer, sbv) @@ -138,6 +148,7 @@ object LmCoursierPlugin extends AutoPlugin { .withClassifiers(classifiers.toVector.flatten) .withHasClassifiers(classifiers.nonEmpty) .withMavenProfiles(profiles.toVector.sorted) + .withReconciliation(versionReconciliations0.toVector) .withScalaOrganization(scalaOrg) .withScalaVersion(scalaVer) .withAuthenticationByRepositoryId(authenticationByRepositoryId.toVector.sortBy(_._1))