Skip to content

Instantly share code, notes, and snippets.

@havocp
Created June 5, 2012 22:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save havocp/2878457 to your computer and use it in GitHub Desktop.
Save havocp/2878457 to your computer and use it in GitHub Desktop.
Patch to support filtering mima issues
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)
@jsuereth
Copy link

jsuereth commented Jun 6, 2012

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment