Skip to content

Commit d165a5c

Browse files
authored
Differentiate unset mimaPreviousArtifacts from empty mimaPreviou… (#334)
Differentiate unset mimaPreviousArtifacts from empty mimaPreviousArtifacts
2 parents c5e3fae + 1471667 commit d165a5c

File tree

8 files changed

+97
-54
lines changed

8 files changed

+97
-54
lines changed

README.md

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -83,33 +83,3 @@ mimaBinaryIssueFilters ++= Seq(
8383
ProblemFilters.exclude[Problem]("com.example.mylibrary.internal.*"),
8484
)
8585
```
86-
87-
## Make mimaReportBinaryIssues not fail
88-
89-
The setting `mimaFailOnNoPrevious` (introduced in v0.4.0) defaults to `true` and will make
90-
`mimaReportBinaryIssues` fail if `mimaPreviousArtifacts` is empty.
91-
92-
To make `mimaReportBinaryIssues` not fail you may want to do one of the following:
93-
94-
* set `mimaPreviousArtifacts` on all the projects that should be checking their binary compatibility
95-
* set `mimaFailOnNoPrevious := false` on specific projects that want to opt-out (alternatively `disablePlugins(MimaPlugin)`)
96-
* set `mimaFailOnNoPrevious in ThisBuild := false`, which disables it build-wide, effectively reverting back to the previous behaviour
97-
98-
You may want to redefine `mimaFailOnNoPrevious` in your build to be conditional on something else. For
99-
instance, if you have already ported your project to Scala 2.13 and set it up for cross-building to Scala 2.13,
100-
but still haven't cut a release, you may want to redefine `mimaFailOnNoPrevious` according to the Scala version,
101-
with something like:
102-
103-
```scala
104-
// fail MiMa if no mimaPreviousArtifacts is set and NOT on Scala 2.13
105-
mimaFailOnNoPrevious in ThisBuild := CrossVersion.partialVersion(scalaVersion.scala) != Some((2, 13))
106-
```
107-
108-
or perhaps using some of sbt 1.2's new API:
109-
110-
```scala
111-
import sbt.librarymanagement.{ SemanticSelector, VersionNumber }
112-
113-
// fail MiMa if no mimaPreviousArtifacts is set and on Scala <2.13
114-
mimaFailOnNoPrevious in ThisBuild := VersionNumber(scalaVersion.value).matchesSemVer(SemanticSelector("<2.13"))
115-
```

sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/MimaKeys.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ object MimaKeys extends MimaKeys
88
class MimaKeys {
99

1010
final val mimaFailOnProblem = settingKey[Boolean]("if true, fail the build on binary incompatibility detection.")
11-
final val mimaFailOnNoPrevious = settingKey[Boolean]("if true, fail the build if no previous artifacts are set.")
1211
final val mimaPreviousArtifacts = settingKey[Set[ModuleID]]("Previous released artifacts used to test binary compatibility.")
1312
final val mimaPreviousClassfiles = taskKey[Map[ModuleID, File]]("Directories or jars containing the previous class files used to test compatibility with a given module.")
1413
final val mimaCurrentClassfiles = taskKey[File]("Directory or jar containing the current class files used to test compatibility.")
@@ -23,4 +22,7 @@ class MimaKeys {
2322

2423
final val mimaCheckDirection = settingKey[String]("Compatibility checking direction; default is \"backward\", but can also be \"forward\" or \"both\".")
2524

25+
@deprecated("No longer used", "0.5.0")
26+
final val mimaFailOnNoPrevious = MimaPlugin.mimaFailOnNoPrevious
27+
2628
}

sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/MimaPlugin.scala

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -52,45 +52,82 @@ object MimaPlugin extends AutoPlugin {
5252
/** Setup mima with default settings, applicable for most projects. */
5353
def mimaDefaultSettings: Seq[Setting[_]] = Seq(
5454
mimaFailOnProblem := true,
55-
mimaPreviousArtifacts := Set.empty,
55+
mimaPreviousArtifacts := NoPreviousArtifacts,
5656
mimaCurrentClassfiles := (classDirectory in Compile).value,
5757
mimaPreviousClassfiles := {
5858
val scalaModuleInfoV = scalaModuleInfo.value
5959
val ivy = ivySbt.value
6060
val taskStreams = streams.value
61-
62-
mimaPreviousArtifacts.value.iterator.map { m =>
63-
val id = CrossVersion(m, scalaModuleInfoV) match {
64-
case Some(f) => m.withName(f(Project.normalizeModuleID(m.name)))
65-
case None => m // no module id normalization if it's fully declaring it (using "%")
66-
}
67-
id -> SbtMima.getPreviousArtifact(id, ivy, taskStreams)
68-
}.toMap
61+
mimaPreviousArtifacts.value match {
62+
case _: NoPreviousArtifacts.type => NoPreviousClassfiles
63+
case previousArtifacts =>
64+
previousArtifacts.iterator.map { m =>
65+
val id = CrossVersion(m, scalaModuleInfoV) match {
66+
case Some(f) => m.withName(f(Project.normalizeModuleID(m.name)))
67+
case None => m // no module id normalization if it's fully declaring it (using "%")
68+
}
69+
id -> SbtMima.getPreviousArtifact(id, ivy, taskStreams)
70+
}.toMap
71+
}
6972
},
7073
fullClasspath in mimaFindBinaryIssues := (fullClasspath in Compile).value
7174
) ++ mimaReportSettings
7275

7376
// Allows reuse between mimaFindBinaryIssues and mimaReportBinaryIssues
7477
// without blowing up the Akka build's heap
7578
private def binaryIssuesIterator = Def.task {
76-
val log = new SbtLogger(streams.value)
79+
val s = streams.value
80+
val log = new SbtLogger(s)
7781
val projectName = name.value
78-
val previousClassfiles = mimaPreviousClassfiles.value
79-
val failOnNoPrevious = mimaFailOnNoPrevious.value
8082
val currentClassfiles = mimaCurrentClassfiles.value
8183
val cp = (fullClasspath in mimaFindBinaryIssues).value
8284
val checkDirection = mimaCheckDirection.value
83-
if (previousClassfiles.isEmpty) {
84-
val msg = s"$projectName: mimaPreviousArtifacts not set, not analyzing binary compatibility. See https://github.com/lightbend/mima#make-mimareportbinaryissues-not-fail"
85-
if (failOnNoPrevious) sys.error(msg)
86-
log.info(msg)
87-
Iterator.empty
88-
}
89-
else {
90-
previousClassfiles.iterator.map { case (moduleId, file) =>
91-
val problems = SbtMima.runMima(file, currentClassfiles, cp, checkDirection, log)
92-
(moduleId, (problems._1, problems._2))
93-
}
85+
mimaPreviousClassfiles.value match {
86+
case _: NoPreviousClassfiles.type =>
87+
sys.error(s"$projectName: mimaPreviousArtifacts not set, not analyzing binary compatibility.")
88+
case previousClassfiles if previousClassfiles.isEmpty =>
89+
s.log.info(s"$projectName: mimaPreviousArtifacts is empty, not analyzing binary compatibility.")
90+
Iterator.empty
91+
case previousClassfiles =>
92+
previousClassfiles.iterator.map { case (moduleId, file) =>
93+
val problems = SbtMima.runMima(file, currentClassfiles, cp, checkDirection, log)
94+
(moduleId, (problems._1, problems._2))
95+
}
9496
}
9597
}
98+
99+
// Used to differentiate unset mimaPreviousArtifacts from empty mimaPreviousArtifacts
100+
private object NoPreviousArtifacts extends EmptySet[ModuleID]
101+
private object NoPreviousClassfiles extends EmptyMap[ModuleID, File]
102+
103+
private sealed class EmptySet[A] extends Set[A] {
104+
def iterator = Iterator.empty
105+
def contains(elem: A) = false
106+
def + (elem: A) = Set(elem)
107+
def - (elem: A) = this
108+
109+
override def size = 0
110+
override def foreach[U](f: A => U) = ()
111+
override def toSet[B >: A]: Set[B] = this.asInstanceOf[Set[B]]
112+
}
113+
114+
private sealed class EmptyMap[K, V] extends Map[K, V] {
115+
def get(key: K) = None
116+
def iterator = Iterator.empty
117+
def + [V1 >: V](kv: (K, V1)) = updated(kv._1, kv._2)
118+
def - (key: K) = this
119+
120+
121+
override def size = 0
122+
override def contains(key: K) = false
123+
override def getOrElse[V1 >: V](key: K, default: => V1) = default
124+
override def updated[V1 >: V](key: K, value: V1) = Map(key -> value)
125+
126+
override def apply(key: K) = throw new NoSuchElementException(s"key not found: $key")
127+
}
128+
129+
// internal un-deprecated version
130+
private[mima] final val mimaFailOnNoPrevious =
131+
settingKey[Boolean]("if true, fail the build if no previous artifacts are set.")
132+
96133
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import com.typesafe.tools.mima.core._
2+
3+
mimaBinaryIssueFilters := Seq(ProblemFilters.exclude[MissingMethodProblem]("A.bar"))
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % System.getProperty("plugin.version"))
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Setup: v2 has a breaking change from v1
2+
> set scalaSource in Compile := baseDirectory.value /"v1"
3+
> set version := s"0.0.1-SNAPSHOT"
4+
> publishLocal
5+
> set scalaSource in Compile := baseDirectory.value /"v2"
6+
> set version := s"0.0.2-SNAPSHOT"
7+
8+
# fail, due to unset mimaPreviousArtifacts
9+
-> mimaReportBinaryIssues
10+
11+
# pass, because mimaPreviousArtifacts was set to the empty set
12+
> set mimaPreviousArtifacts := Set.empty
13+
> mimaReportBinaryIssues
14+
> set mimaPreviousArtifacts := Set()
15+
> mimaReportBinaryIssues
16+
> set mimaPreviousArtifacts := scala.collection.immutable.HashSet()
17+
> mimaReportBinaryIssues
18+
> set mimaPreviousArtifacts := scala.collection.immutable.HashSet.empty
19+
> mimaReportBinaryIssues
20+
21+
# pass, because mimaPreviousArtifacts was set and filters are set in the build file
22+
> set mimaPreviousArtifacts := Set(organization.value %% name.value % s"0.0.1-SNAPSHOT")
23+
> mimaReportBinaryIssues
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class A {
2+
def foo = 1
3+
def bar = foo
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class A {
2+
def foo = 1
3+
}

0 commit comments

Comments
 (0)