From f9479702476d262e5f375c1443d76f6a94769c0d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 5 Aug 2020 00:21:51 -0400 Subject: [PATCH] versionScheme setting + better eviction warning Ref https://github.com/sbt/sbt/issues/5710 Ref https://github.com/sbt/librarymanagement/pull/339 This adds `versionScheme` setting. When set, it is included into POM, and gets picked up on the other side as an extra attribute of ModuleID. That information in turn is used to inform the eviction warning. This should reduce the false positives associated with SemVer'ed libraries showing up in the eviction warning. --- main/src/main/scala/sbt/Defaults.scala | 18 ++++-- main/src/main/scala/sbt/Keys.scala | 1 + .../sbt/coursierint/CoursierInputsTasks.scala | 15 +++-- project/Dependencies.scala | 2 +- .../evicted-semver-spec/build.sbt | 62 +++++++++++++++++++ .../evicted-semver-spec/test | 5 ++ .../v1.0.0/LibraryTest.scala | 3 + .../v1.1.0/LibraryTest.scala | 3 + 8 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 sbt/src/sbt-test/dependency-management/evicted-semver-spec/build.sbt create mode 100644 sbt/src/sbt-test/dependency-management/evicted-semver-spec/test create mode 100644 sbt/src/sbt-test/dependency-management/evicted-semver-spec/v1.0.0/LibraryTest.scala create mode 100644 sbt/src/sbt-test/dependency-management/evicted-semver-spec/v1.1.0/LibraryTest.scala diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 487ee7958..1f046a7e5 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -305,6 +305,7 @@ object Defaults extends BuildCommon { crossVersion :== Disabled(), buildDependencies := Classpaths.constructBuildDependencies.value, version :== "0.1.0-SNAPSHOT", + versionScheme :== None, classpathTypes :== Set("jar", "bundle", "maven-plugin", "test-jar") ++ CustomPomParser.JarPackagings, artifactClassifier :== None, checksums := Classpaths.bootChecksums(appConfiguration.value), @@ -2645,7 +2646,7 @@ object Classpaths { val report = (updateTask tag (Tags.Update, Tags.Network)).value val log = streams.value.log val ew = - EvictionWarning(ivyModule.value, (evictionWarningOptions in evicted).value, report) + EvictionWarning(ivyModule.value, (evicted / evictionWarningOptions).value, report) ew.lines foreach { log.warn(_) } ew.infoAllTheThings foreach { log.info(_) } ew @@ -2763,13 +2764,20 @@ object Classpaths { } private[sbt] def defaultProjectID: Initialize[ModuleID] = Def.setting { - val base = ModuleID(organization.value, moduleName.value, version.value) + val p0 = ModuleID(organization.value, moduleName.value, version.value) .cross(crossVersion in projectID value) .artifacts(artifacts.value: _*) - apiURL.value match { - case Some(u) => base.extra(SbtPomExtraProperties.POM_API_KEY -> u.toExternalForm) - case _ => base + val p1 = apiURL.value match { + case Some(u) => p0.extra(SbtPomExtraProperties.POM_API_KEY -> u.toExternalForm) + case _ => p0 } + val p2 = versionScheme.value match { + case Some(x) => + VersionSchemes.validateScheme(x) + p1.extra(SbtPomExtraProperties.VERSION_SCHEME_KEY -> x) + case _ => p1 + } + p2 } def pluginProjectID: Initialize[ModuleID] = Def.setting { diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index 12a28d56c..7333ac8a1 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -488,6 +488,7 @@ object Keys { val packagedArtifact = taskKey[(Artifact, File)]("Generates a packaged artifact, returning the Artifact and the produced File.").withRank(CTask) val checksums = settingKey[Seq[String]]("The list of checksums to generate and to verify for dependencies.").withRank(BSetting) val forceUpdatePeriod = settingKey[Option[FiniteDuration]]("Duration after which to force a full update to occur").withRank(CSetting) + val versionScheme = settingKey[Option[String]]("""Version scheme used for the subproject: Supported values are Some("early-semver"), Some("pvp"), and Some("semver-spec")""").withRank(BSetting) val classifiersModule = taskKey[GetClassifiersModule]("classifiers-module").withRank(CTask) val compatibilityWarningOptions = settingKey[CompatibilityWarningOptions]("Configures warnings around Maven incompatibility.").withRank(CSetting) diff --git a/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala b/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala index 42eb7d173..9059f36ef 100644 --- a/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala +++ b/main/src/main/scala/sbt/coursierint/CoursierInputsTasks.scala @@ -28,6 +28,7 @@ import lmcoursier.definitions.{ } import lmcoursier.credentials.DirectCredentials import lmcoursier.{ FallbackDependency, FromSbt, Inputs } +import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties import sbt.librarymanagement.ivy.{ FileCredentials, Credentials, @@ -46,6 +47,7 @@ object CoursierInputsTasks { auOpt: Option[URL], description: String, homepage: Option[URL], + vsOpt: Option[String], log: Logger ): CProject = { @@ -60,12 +62,16 @@ object CoursierInputsTasks { ) val proj1 = auOpt match { case Some(au) => - val props = proj0.properties :+ ("info.apiURL" -> au.toString) - proj0.withProperties(props) + proj0.withProperties(proj0.properties :+ (SbtPomExtraProperties.POM_API_KEY -> au.toString)) case _ => proj0 } - proj1.withInfo( - proj1.info.withDescription(description).withHomePage(homepage.fold("")(_.toString)) + val proj2 = vsOpt match { + case Some(vs) => + proj1.withProperties(proj1.properties :+ (SbtPomExtraProperties.VERSION_SCHEME_KEY -> vs)) + case _ => proj1 + } + proj2.withInfo( + proj2.info.withDescription(description).withHomePage(homepage.fold("")(_.toString)) ) } @@ -80,6 +86,7 @@ object CoursierInputsTasks { apiURL.value, description.value, homepage.value, + versionScheme.value, streams.value.log ) } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index e640a014d..2df70417c 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -13,7 +13,7 @@ object Dependencies { // sbt modules private val ioVersion = nightlyVersion.getOrElse("1.4.0-M6") private val lmVersion = - sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.4.0-M1") + sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.4.0-M2") val zincVersion = nightlyVersion.getOrElse("1.4.0-M7") private val sbtIO = "org.scala-sbt" %% "io" % ioVersion diff --git a/sbt/src/sbt-test/dependency-management/evicted-semver-spec/build.sbt b/sbt/src/sbt-test/dependency-management/evicted-semver-spec/build.sbt new file mode 100644 index 000000000..d8824283f --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/evicted-semver-spec/build.sbt @@ -0,0 +1,62 @@ +// ThisBuild / useCoursier := false +ThisBuild / organization := "com.example" +ThisBuild / scalaVersion := "2.12.11" +ThisBuild / versionScheme := Some("semver-spec") + +def commonSettings: Seq[Def.Setting[_]] = + Seq( + ivyPaths := IvyPaths( + (ThisBuild / baseDirectory).value, + Some((LocalRootProject / target).value / "ivy-cache") + ), + csrCacheDirectory := (LocalRootProject / target).value / "cache", + fullResolvers := fullResolvers.value.filterNot(_.name == "inter-project"), + publishTo := Some(MavenCache("local-maven", (LocalRootProject / target).value / "local-maven")), + resolvers += MavenCache("local-maven", (LocalRootProject / target).value / "local-maven"), + ) + +lazy val root = (project in file(".")) + .settings(commonSettings) + +val `v1-0-0` = (project in file("v1.0.0")) + .settings(commonSettings) + .settings( + name := "semver-spec-test", + version := "1.0.0", + ) + +val `v1-1-0` = (project in file("v1.1.0")) + .settings(commonSettings) + .settings( + name := "semver-spec-test", + version := "1.1.0", + ) + +val middle = project + .settings(commonSettings) + .settings( + name := "middle", + version := "1.0.0", + libraryDependencies += "com.example" %% "semver-spec-test" % "1.0.0", + ) + +val use = project + .settings(commonSettings) + .settings( + name := "use", + libraryDependencies ++= Seq( + "com.example" %% "semver-spec-test" % "1.1.0", + "com.example" %% "middle" % "1.0.0", + ), + TaskKey[Unit]("check") := { + val report = updateFull.value + val log = streams.value.log + val extraAttributes = (report.allModules flatMap { _.extraAttributes}) collect { + case ("info.versionScheme", v) => v + } + log.info(s"extraAttributes = $extraAttributes") + assert(extraAttributes.nonEmpty, s"$extraAttributes is empty") + val ew = EvictionWarning(ivyModule.value, (evicted / evictionWarningOptions).value, report) + assert(ew.directEvictions.isEmpty, s"${ew.directEvictions} is not empty") + }, + ) diff --git a/sbt/src/sbt-test/dependency-management/evicted-semver-spec/test b/sbt/src/sbt-test/dependency-management/evicted-semver-spec/test new file mode 100644 index 000000000..eb5f0c784 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/evicted-semver-spec/test @@ -0,0 +1,5 @@ +> v1-0-0/publish +> v1-1-0/publish +> middle/publish + +> use/check diff --git a/sbt/src/sbt-test/dependency-management/evicted-semver-spec/v1.0.0/LibraryTest.scala b/sbt/src/sbt-test/dependency-management/evicted-semver-spec/v1.0.0/LibraryTest.scala new file mode 100644 index 000000000..fbfda9e74 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/evicted-semver-spec/v1.0.0/LibraryTest.scala @@ -0,0 +1,3 @@ +package example + +trait Foo {} diff --git a/sbt/src/sbt-test/dependency-management/evicted-semver-spec/v1.1.0/LibraryTest.scala b/sbt/src/sbt-test/dependency-management/evicted-semver-spec/v1.1.0/LibraryTest.scala new file mode 100644 index 000000000..fbfda9e74 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/evicted-semver-spec/v1.1.0/LibraryTest.scala @@ -0,0 +1,3 @@ +package example + +trait Foo {}