diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/CoursierConfiguration.scala b/modules/lm-coursier/src/main/scala/lmcoursier/CoursierConfiguration.scala index c2fe434f7..69a209c67 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/CoursierConfiguration.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/CoursierConfiguration.scala @@ -8,7 +8,7 @@ package lmcoursier import java.io.File import lmcoursier.credentials.Credentials -import lmcoursier.definitions.{Authentication, CacheLogger, Project} +import lmcoursier.definitions.{Authentication, CacheLogger, Project, Strict} import sbt.librarymanagement.Resolver import xsbti.Logger @@ -34,7 +34,8 @@ final class CoursierConfiguration private ( val logger: Option[CacheLogger], val cache: Option[File], val ivyHome: Option[File], - val followHttpToHttpsRedirections: Option[Boolean] + val followHttpToHttpsRedirections: Option[Boolean], + val strict: Option[Strict] ) extends Serializable { private def this() = @@ -60,6 +61,57 @@ final class CoursierConfiguration private ( None, None, None, + None, + None + ) + + def this( + log: Option[Logger], + resolvers: Vector[Resolver], + parallelDownloads: Int, + maxIterations: Int, + sbtScalaOrganization: Option[String], + sbtScalaVersion: Option[String], + sbtScalaJars: Vector[File], + interProjectDependencies: Vector[Project], + excludeDependencies: Vector[(String, String)], + fallbackDependencies: Vector[FallbackDependency], + autoScalaLibrary: Boolean, + hasClassifiers: Boolean, + classifiers: Vector[String], + mavenProfiles: Vector[String], + scalaOrganization: Option[String], + scalaVersion: Option[String], + authenticationByRepositoryId: Vector[(String, Authentication)], + credentials: Seq[Credentials], + logger: Option[CacheLogger], + cache: Option[File], + ivyHome: Option[File], + followHttpToHttpsRedirections: Option[Boolean] + ) = + this( + log, + resolvers, + parallelDownloads, + maxIterations, + sbtScalaOrganization, + sbtScalaVersion, + sbtScalaJars, + interProjectDependencies, + excludeDependencies, + fallbackDependencies, + autoScalaLibrary, + hasClassifiers, + classifiers, + mavenProfiles, + scalaOrganization, + scalaVersion, + authenticationByRepositoryId, + credentials, + logger, + cache, + ivyHome, + followHttpToHttpsRedirections, None ) @@ -87,7 +139,8 @@ final class CoursierConfiguration private ( logger == other.logger && cache == other.cache && ivyHome == other.ivyHome && - followHttpToHttpsRedirections == other.followHttpToHttpsRedirections + followHttpToHttpsRedirections == other.followHttpToHttpsRedirections && + strict == other.strict case _ => false } @@ -115,11 +168,12 @@ final class CoursierConfiguration private ( code = 37 * (code + cache.##) code = 37 * (code + ivyHome.##) code = 37 * (code + followHttpToHttpsRedirections.##) + code = 37 * (code + strict.##) code } override def toString: String = - s"CoursierConfiguration($log, $resolvers, $parallelDownloads, $maxIterations, $sbtScalaOrganization, $sbtScalaVersion, $sbtScalaJars, $interProjectDependencies, $excludeDependencies, $fallbackDependencies, $autoScalaLibrary, $hasClassifiers, $classifiers, $mavenProfiles, $scalaOrganization, $scalaVersion, $authenticationByRepositoryId, $credentials, $logger, $cache, $ivyHome, $followHttpToHttpsRedirections)" + s"CoursierConfiguration($log, $resolvers, $parallelDownloads, $maxIterations, $sbtScalaOrganization, $sbtScalaVersion, $sbtScalaJars, $interProjectDependencies, $excludeDependencies, $fallbackDependencies, $autoScalaLibrary, $hasClassifiers, $classifiers, $mavenProfiles, $scalaOrganization, $scalaVersion, $authenticationByRepositoryId, $credentials, $logger, $cache, $ivyHome, $followHttpToHttpsRedirections, $strict)" private[this] def copy( log: Option[Logger] = log, @@ -143,7 +197,8 @@ final class CoursierConfiguration private ( logger: Option[CacheLogger] = logger, cache: Option[File] = cache, ivyHome: Option[File] = ivyHome, - followHttpToHttpsRedirections: Option[Boolean] = followHttpToHttpsRedirections + followHttpToHttpsRedirections: Option[Boolean] = followHttpToHttpsRedirections, + strict: Option[Strict] = strict ): CoursierConfiguration = new CoursierConfiguration( log, @@ -167,7 +222,8 @@ final class CoursierConfiguration private ( logger, cache, ivyHome, - followHttpToHttpsRedirections + followHttpToHttpsRedirections, + strict ) def withLog(log: Option[Logger]): CoursierConfiguration = @@ -262,6 +318,11 @@ final class CoursierConfiguration private ( copy(followHttpToHttpsRedirections = Some(followHttpToHttpsRedirections)) def withFollowHttpToHttpsRedirections(): CoursierConfiguration = copy(followHttpToHttpsRedirections = Some(true)) + + def withStrict(strict: Strict): CoursierConfiguration = + copy(strict = Some(strict)) + def withStrict(strictOpt: Option[Strict]): CoursierConfiguration = + copy(strict = strictOpt) } object CoursierConfiguration { @@ -313,6 +374,7 @@ object CoursierConfiguration { logger, cache, None, + None, None ) @@ -360,6 +422,7 @@ object CoursierConfiguration { Option(logger), Option(cache), None, + None, None ) @@ -408,6 +471,7 @@ object CoursierConfiguration { logger, cache, ivyHome, + None, None ) } diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala b/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala index 3aa0c2622..69c440b20 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala @@ -142,7 +142,8 @@ class CoursierDependencyResolution(conf: CoursierConfiguration) extends Dependen .withMaxIterations(conf.maxIterations) .withProfiles(conf.mavenProfiles.toSet) .withForceVersion(Map.empty) - .withTypelevel(typelevel) + .withTypelevel(typelevel), + strictOpt = conf.strict.map(ToCoursier.strict) ) def artifactsParams(resolutions: Map[Set[Configuration], Resolution]): ArtifactsParams = diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/definitions/Strict.scala b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/Strict.scala new file mode 100644 index 000000000..c1ea79afc --- /dev/null +++ b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/Strict.scala @@ -0,0 +1,55 @@ +package lmcoursier.definitions + +final class Strict private ( + val include: Set[(String, String)], + val exclude: Set[(String, String)], + val ignoreIfForcedVersion: Boolean +) { + + override def equals(obj: Any): Boolean = + obj match { + case other: Strict => + include == other.include && + exclude == other.exclude && + ignoreIfForcedVersion == other.ignoreIfForcedVersion + case _ => + false + } + + override def hashCode(): Int = { + var code = 17 + "lmcoursier.definitions.Strict".## + code = 37 * code + include.## + code = 37 * code + exclude.## + code = 37 * code + ignoreIfForcedVersion.## + 37 * code + } + + override def toString: String = + s"Strict($include, $exclude, $ignoreIfForcedVersion)" + + private def copy( + include: Set[(String, String)] = include, + exclude: Set[(String, String)] = exclude, + ignoreIfForcedVersion: Boolean = ignoreIfForcedVersion + ): Strict = + new Strict(include, exclude, ignoreIfForcedVersion) + + + def withInclude(include: Set[(String, String)]): Strict = + copy(include = include) + def addInclude(include: (String, String)*): Strict = + copy(include = this.include ++ include) + + def withExclude(exclude: Set[(String, String)]): Strict = + copy(exclude = exclude) + def addExclude(exclude: (String, String)*): Strict = + copy(exclude = this.exclude ++ exclude) + + def withIgnoreIfForcedVersion(ignoreIfForcedVersion: Boolean): Strict = + copy(ignoreIfForcedVersion = ignoreIfForcedVersion) +} + +object Strict { + def apply(): Strict = + new Strict(Set(("*", "*")), Set.empty, ignoreIfForcedVersion = true) +} 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 2db5c8968..46cb67bf0 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/definitions/ToCoursier.scala @@ -1,5 +1,6 @@ package lmcoursier.definitions +import coursier.util.ModuleMatcher import lmcoursier.credentials.{Credentials, DirectCredentials, FileCredentials} // TODO Make private[lmcoursier] @@ -142,4 +143,15 @@ object ToCoursier { logger.stop() } + def strict(strict: Strict): coursier.params.rule.Strict = + coursier.params.rule.Strict( + include = strict.include.map { + case (o, n) => ModuleMatcher(coursier.Module(coursier.Organization(o), coursier.ModuleName(n))) + }, + exclude = strict.exclude.map { + case (o, n) => ModuleMatcher(coursier.Module(coursier.Organization(o), coursier.ModuleName(n))) + }, + // ignoreIfForcedVersion = strict.ignoreIfForcedVersion // should be around once the coursier version is bumped + ) + } diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionParams.scala b/modules/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionParams.scala index 2dd3299c2..c71765dfa 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionParams.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionParams.scala @@ -5,9 +5,10 @@ import java.io.File import coursier.cache.{CacheLogger, FileCache} import coursier.ProjectCache import coursier.core._ +import coursier.params.rule.Strict import lmcoursier.FallbackDependency import lmcoursier.definitions.ToCoursier -import coursier.util.{InMemoryRepository, Task} +import coursier.util.Task // private[coursier] final case class ResolutionParams( @@ -24,7 +25,8 @@ final case class ResolutionParams( loggerOpt: Option[CacheLogger], cache: coursier.cache.FileCache[Task], parallel: Int, - params: coursier.params.ResolutionParams + params: coursier.params.ResolutionParams, + strictOpt: Option[Strict] ) { val fallbackDependenciesRepositories = diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala b/modules/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala index 4a7edf6f2..d7b70912c 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/internal/ResolutionRun.scala @@ -6,6 +6,7 @@ import coursier.cache.loggers.{FallbackRefreshDisplay, ProgressBarRefreshDisplay import coursier.core._ import coursier.ivy.IvyRepository import coursier.maven.MavenRepository +import coursier.params.rule.RuleResolution import sbt.util.Logger // private[coursier] @@ -18,6 +19,8 @@ object ResolutionRun { configs: Set[Configuration] ): Either[coursier.error.ResolutionError, Resolution] = { + val rules = params.strictOpt.map(s => Seq((s, RuleResolution.Fail))).getOrElse(Nil) + val isCompileConfig = configs(Configuration.compile) || configs(Configuration("scala-tool")) @@ -81,6 +84,7 @@ object ResolutionRun { .withForceScalaVersion(isCompileConfig && params.autoScalaLibOpt.nonEmpty) .withScalaVersion(params.autoScalaLibOpt.map(_._2)) .withTypelevel(params.params.typelevel && isCompileConfig) + .withRules(rules) ) .withCache( params diff --git a/modules/sbt-coursier-shared/src/main/scala/coursier/sbtcoursiershared/InputsTasks.scala b/modules/sbt-coursier-shared/src/main/scala/coursier/sbtcoursiershared/InputsTasks.scala index 8dbe901c9..dc77eeb01 100644 --- a/modules/sbt-coursier-shared/src/main/scala/coursier/sbtcoursiershared/InputsTasks.scala +++ b/modules/sbt-coursier-shared/src/main/scala/coursier/sbtcoursiershared/InputsTasks.scala @@ -1,13 +1,13 @@ package coursier.sbtcoursiershared -import lmcoursier.definitions.{Attributes, Classifier, Configuration, Dependency, Info, Module, ModuleName, Organization, Project, Type} +import lmcoursier.definitions.{Attributes, Classifier, Configuration, Dependency, Info, Module, ModuleName, Organization, Project, Strict, Type} import lmcoursier.{FallbackDependency, FromSbt, Inputs} import coursier.sbtcoursiershared.SbtCoursierShared.autoImport._ import coursier.sbtcoursiershared.Structure._ import lmcoursier.credentials.DirectCredentials import sbt.{Def, SettingKey} import sbt.Keys._ -import sbt.librarymanagement.{InclExclRule, ModuleID} +import sbt.librarymanagement.{ConflictManager, InclExclRule, ModuleID} import sbt.util.Logger import scala.collection.JavaConverters._ @@ -257,4 +257,23 @@ object InputsTasks { } } + + def strictTask = Def.task { + val cm = conflictManager.value + val log = streams.value.log + + cm.name match { + case ConflictManager.latestRevision.name => + None + case ConflictManager.strict.name => + val strict = Strict() + .withInclude(Set((cm.organization, cm.module))) + Some(strict) + case other => + log.warn(s"Unsupported conflict manager $other") + None + } + } + + } 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 fc20911bb..c0c021352 100644 --- a/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/ResolutionTasks.scala +++ b/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/ResolutionTasks.scala @@ -4,14 +4,15 @@ import coursier.ProjectCache import coursier.cache.FileCache import coursier.core._ import coursier.internal.Typelevel -import lmcoursier.definitions.ToCoursier +import lmcoursier.definitions.{Strict, ToCoursier} import lmcoursier.{FallbackDependency, FromSbt} import lmcoursier.internal.{InterProjectRepository, ResolutionParams, ResolutionRun, Resolvers} import coursier.sbtcoursier.Keys._ -import coursier.sbtcoursiershared.InputsTasks.credentialsTask +import coursier.sbtcoursiershared.InputsTasks.{credentialsTask, strictTask} import coursier.sbtcoursiershared.SbtCoursierShared.autoImport._ import sbt.Def import sbt.Keys._ +import sbt.librarymanagement.ConflictManager object ResolutionTasks { @@ -100,6 +101,8 @@ object ResolutionTasks { val credentials = credentialsTask.value.map(ToCoursier.credentials) + val strictOpt = strictTask.value.map(ToCoursier.strict) + val parentProjectCache: ProjectCache = coursierParentProjectCache.value .get(resolvers) .map(_.foldLeft[ProjectCache](Map.empty)(_ ++ _)) @@ -144,7 +147,8 @@ object ResolutionTasks { .withMaxIterations(maxIterations) .withProfiles(userEnabledProfiles) .withForceVersion(userForceVersions) - .withTypelevel(typelevel) + .withTypelevel(typelevel), + strictOpt = strictOpt ), verbosityLevel, log diff --git a/modules/sbt-coursier/src/sbt-test/shared-2/strict-conflict-manager/build.sbt b/modules/sbt-coursier/src/sbt-test/shared-2/strict-conflict-manager/build.sbt new file mode 100644 index 000000000..051999109 --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/shared-2/strict-conflict-manager/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" + ), + conflictManager := ConflictManager.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/strict-conflict-manager/project/plugins.sbt b/modules/sbt-coursier/src/sbt-test/shared-2/strict-conflict-manager/project/plugins.sbt new file mode 100644 index 000000000..71a44ffd3 --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/shared-2/strict-conflict-manager/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/strict-conflict-manager/test b/modules/sbt-coursier/src/sbt-test/shared-2/strict-conflict-manager/test new file mode 100644 index 000000000..4599d10e8 --- /dev/null +++ b/modules/sbt-coursier/src/sbt-test/shared-2/strict-conflict-manager/test @@ -0,0 +1,3 @@ +-> a/update +# enable once when we bump the coursier version +# > 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 7bc0dd914..87dc2f5e0 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 @@ -2,7 +2,7 @@ package coursier.sbtlmcoursier import lmcoursier.definitions.Authentication import lmcoursier.{CoursierConfiguration, CoursierDependencyResolution, Inputs} -import coursier.sbtcoursiershared.InputsTasks.credentialsTask +import coursier.sbtcoursiershared.InputsTasks.{credentialsTask, strictTask} import coursier.sbtcoursiershared.{InputsTasks, SbtCoursierShared} import sbt.{AutoPlugin, Classpaths, Def, Setting, Task, taskKey} import sbt.Project.inTask @@ -94,6 +94,7 @@ object LmCoursierPlugin extends AutoPlugin { Authentication(a.user, a.password, a.optional, a.realmOpt) } val credentials = credentialsTask.value + val strict = strictTask.value val createLogger = coursierLogger.value @@ -134,6 +135,7 @@ object LmCoursierPlugin extends AutoPlugin { .withCache(cache) .withLog(s.log) .withIvyHome(ivyPaths.value.ivyHome) + .withStrict(strict) } } private def mkDependencyResolution: Def.Initialize[Task[DependencyResolution]] = diff --git a/project/Mima.scala b/project/Mima.scala index 7326f5eea..14c69b5b2 100644 --- a/project/Mima.scala +++ b/project/Mima.scala @@ -37,7 +37,9 @@ object Mima { Seq( // Removed unused method, shouldn't have been there in the first place - ProblemFilters.exclude[DirectMissingMethodProblem]("lmcoursier.credentials.DirectCredentials.authentication") + ProblemFilters.exclude[DirectMissingMethodProblem]("lmcoursier.credentials.DirectCredentials.authentication"), + // ignore shaded and internal stuff related errors + (pb: Problem) => pb.matchName.forall(!_.startsWith("lmcoursier.internal.")) ) } } @@ -48,9 +50,7 @@ object Mima { Seq( // Should have been put under lmcoursier.internal? - (pb: Problem) => pb.matchName.forall(!_.startsWith("lmcoursier.definitions.ToCoursier.")), - // ignore shaded and internal stuff related errors - (pb: Problem) => pb.matchName.forall(!_.startsWith("lmcoursier.internal.")) + (pb: Problem) => pb.matchName.forall(!_.startsWith("lmcoursier.definitions.ToCoursier.")) ) } }