public
Created

Patch to support filtering mima issues

  • Download Gist
filter-reported-issues.patch
Diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
diff --git a/core/src/main/scala/com/typesafe/tools/mima/core/Filters.scala b/core/src/main/scala/com/typesafe/tools/mima/core/Filters.scala
new file mode 100644
index 0000000..fc8b98c
--- /dev/null
+++ b/core/src/main/scala/com/typesafe/tools/mima/core/Filters.scala
@@ -0,0 +1,17 @@
+package com.typesafe.tools.mima.core
+
+object ProblemFilters {
+
+ private case class ExcludeByName[P <: ProblemRef: ClassManifest](val name: String) extends ProblemFilter {
+ override def apply(problem: Problem): Boolean = {
+ !(implicitly[ClassManifest[P]].erasure.isAssignableFrom(problem.getClass) &&
+ Some(name) == problem.matchName)
+ }
+
+ override def toString(): String = """ExcludeByName[%s]("%s")""".format(implicitly[ClassManifest[P]].erasure.getSimpleName, name)
+ }
+
+ def exclude[P <: ProblemRef: ClassManifest](name: String): ProblemFilter = {
+ ExcludeByName[P](name)
+ }
+}
diff --git a/core/src/main/scala/com/typesafe/tools/mima/core/Problems.scala b/core/src/main/scala/com/typesafe/tools/mima/core/Problems.scala
index e8ed5e4..07b2018 100644
--- a/core/src/main/scala/com/typesafe/tools/mima/core/Problems.scala
+++ b/core/src/main/scala/com/typesafe/tools/mima/core/Problems.scala
@@ -12,6 +12,14 @@ trait ProblemRef {
def ref: Ref
def fileName: String
def referredMember: String
+
+ // name that can be used to write a matching filter
+ def matchName: Option[String] = None
+
+ // description of how to make a filter rule
+ def howToFilter: Option[String] = matchName map { name =>
+ """ProblemFilters.exclude[%s]("%s")""".format(this.getClass.getSimpleName, name)
+ }
}
trait TemplateRef extends ProblemRef {
@@ -31,9 +39,13 @@ sealed abstract class Problem extends ProblemRef {
def description: String
}
-abstract class TemplateProblem(override val ref: ClassInfo) extends Problem with TemplateRef
+abstract class TemplateProblem(override val ref: ClassInfo) extends Problem with TemplateRef {
+ override def matchName = Some(ref.fullName)
+}
-abstract class MemberProblem(override val ref: MemberInfo) extends Problem with MemberRef
+abstract class MemberProblem(override val ref: MemberInfo) extends Problem with MemberRef {
+ override def matchName = Some(referredMember)
+}
case class MissingFieldProblem(oldfld: MemberInfo) extends MemberProblem(oldfld) {
def description = oldfld.fieldString + " does not have a correspondent in " + affectedVersion + " version"
@@ -119,4 +131,4 @@ case class InaccessibleMethodProblem(newmeth: MemberInfo) extends MemberProblem(
case class InaccessibleClassProblem(newclazz: ClassInfo) extends TemplateProblem(newclazz) {
def description = newclazz.classString + " was public; is inaccessible in " + affectedVersion + " version"
-}
\ No newline at end of file
+}
diff --git a/core/src/main/scala/com/typesafe/tools/mima/core/package.scala b/core/src/main/scala/com/typesafe/tools/mima/core/package.scala
new file mode 100644
index 0000000..481dcb0
--- /dev/null
+++ b/core/src/main/scala/com/typesafe/tools/mima/core/package.scala
@@ -0,0 +1,5 @@
+package com.typesafe.tools.mima
+
+package object core {
+ type ProblemFilter = (Problem) => Boolean
+}
diff --git a/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/Keys.scala b/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/Keys.scala
index 3e7f1e7..89d3d5d 100644
--- a/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/Keys.scala
+++ b/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/Keys.scala
@@ -12,5 +12,5 @@ object MimaKeys {
// TODO - Create a task to make a MiMaLib, is that a good idea?
val findBinaryIssues = TaskKey[List[core.Problem]]("mima-find-binary-issues", "A list of all binary incompatibilities between two files.")
val reportBinaryIssues = TaskKey[Unit]("mima-report-binary-issues", "Logs all binary incompatibilities to the sbt console/logs.")
-
+ val binaryIssueFilters = SettingKey[Seq[core.ProblemFilter]]("mima-binary-issue-filters", "A list of filters to apply to binary issues found.")
}
diff --git a/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/MimaPlugin.scala b/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/MimaPlugin.scala
index 88b362e..c844c78 100644
--- a/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/MimaPlugin.scala
+++ b/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/MimaPlugin.scala
@@ -9,6 +9,7 @@ object MimaPlugin extends Plugin {
import MimaKeys._
/** Just configures MiMa to compare previous/current classfiles.*/
def mimaReportSettings: Seq[Setting[_]] = Seq(
+ binaryIssueFilters := Nil,
findBinaryIssues <<= (previousClassfiles, currentClassfiles,
fullClasspath in findBinaryIssues, streams, name) map { (prevOption, curr, cp, s, name) =>
prevOption match {
@@ -19,7 +20,7 @@ object MimaPlugin extends Plugin {
Nil
}
},
- reportBinaryIssues <<= (findBinaryIssues, failOnProblem, streams, name) map SbtMima.reportErrors)
+ reportBinaryIssues <<= (findBinaryIssues, failOnProblem, binaryIssueFilters, streams, name) map SbtMima.reportErrors)
/** Setup mima with default settings, applicable for most projects. */
def mimaDefaultSettings: Seq[Setting[_]] = Seq(
failOnProblem := true,
diff --git a/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala b/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala
index f019771..81cd504 100644
--- a/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala
+++ b/sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala
@@ -34,10 +34,26 @@ object SbtMima {
/** Reports binary compatibility errors.
* @param failOnProblem if true, fails the build on binary compatibility errors.
*/
- def reportErrors(errors: List[core.Problem], failOnProblem: Boolean, s: TaskStreams, projectName: String): Unit = {
+ def reportErrors(found: List[core.Problem], failOnProblem: Boolean, filters: Seq[core.ProblemFilter], s: TaskStreams, projectName: String): Unit = {
+ // this is sort of n-squared, it's fixable in principle by special-casing known
+ // filter types or something, not worth it most likely...
+ val errors = found filter { problem =>
+ filters.foldLeft(true)({ (sofar, f) =>
+ val keep = sofar && f(problem)
+ if (sofar && !keep)
+ s.log.debug(projectName + ": filtered out: " + problem.description + "\n filtered by: " + f)
+ keep
+ })
+ }
+
+ val filteredCount = found.size - errors.size
+ val filteredNote = if (filteredCount > 0) " (filtered " + filteredCount + ")" else ""
+
// TODO - Line wrapping an other magikz
- def prettyPrint(p: core.Problem): String = " * " + p.description
- s.log.info(projectName + ": found " + errors.size + " potential binary incompatibilities")
+ def prettyPrint(p: core.Problem): String = {
+ " * " + p.description + p.howToFilter.map("\n filter with: " + _).getOrElse("")
+ }
+ s.log.info(projectName + ": found " + errors.size + " potential binary incompatibilities" + filteredNote)
errors map prettyPrint foreach { p =>
if (failOnProblem) s.log.error(p)
else s.log.warn(p)

Any reason for using the foldLeft instead of just finding the first filter with something like 'exists' or using a for expression?

i.e.

def isFiltered(p: Problem): Boolean = filter exists { 
  case f if f(p) => 
    s.log.debug(projectName + ": filtered out: " + problem.description + "\n filtered by: " + f)
    true
  case _ => false
}
val errors = found filter isFiltered

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.