Skip to content
Browse files

Merge pull request #12 from havocp/havocp-filter-problems

mima-binary-issue-filters key
  • Loading branch information...
2 parents 8f76fbc + 34d8b4f commit e5f84a4a95b402b1692b304999a8ef8dcea8ee5d @jsuereth jsuereth committed
View
17 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](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)
+ }
+}
View
18 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"
-}
+}
View
5 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
+}
View
2 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.")
}
View
28 sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/MimaPlugin.scala
@@ -2,26 +2,25 @@ package com.typesafe.tools.mima
package plugin
import sbt._
-import sbt.Keys.{fullClasspath, streams, classDirectory, ivySbt, name}
+import sbt.Keys.{ fullClasspath, streams, classDirectory, ivySbt, name }
/** Sbt plugin for using MiMa. */
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 {
- case Some(prev) =>
- SbtMima.runMima(prev, curr, cp, s)
- case None =>
- s.log.info(name + ": previous-artifact not set, not analyzing binary compatibility")
- Nil
- }
- },
- reportBinaryIssues <<= (findBinaryIssues, failOnProblem, streams, name) map SbtMima.reportErrors
- )
+ fullClasspath in findBinaryIssues, streams, name) map { (prevOption, curr, cp, s, name) =>
+ prevOption match {
+ case Some(prev) =>
+ SbtMima.runMima(prev, curr, cp, s)
+ case None =>
+ s.log.info(name + ": previous-artifact not set, not analyzing binary compatibility")
+ Nil
+ }
+ },
+ 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,
@@ -30,6 +29,5 @@ object MimaPlugin extends Plugin {
previousClassfiles <<= (ivySbt, previousArtifact, streams) map { (i, optArt, s) =>
optArt map { art => SbtMima.getPreviousArtifact(art, i, s) }
},
- fullClasspath in findBinaryIssues <<= fullClasspath in Compile
- ) ++ mimaReportSettings
+ fullClasspath in findBinaryIssues <<= fullClasspath in Compile) ++ mimaReportSettings
}
View
47 sbtplugin/src/main/scala/com/typesafe/tools/mima/plugin/SbtMima.scala
@@ -32,36 +32,53 @@ object SbtMima {
makeMima(cp, s).collectProblems(prev.getAbsolutePath, curr.getAbsolutePath)
/** Reports binary compatibility errors.
- * @param failOnProblem if true, fails the build on 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 = {
+ // filters * found is n-squared, it's fixable in principle by special-casing known
+ // filter types or something, not worth it most likely...
+
+ def isReported(problem: core.Problem) = filters forall { f =>
+ if (f(problem)) {
+ true
+ } else {
+ s.log.debug(projectName + ": filtered out: " + problem.description + "\n filtered by: " + f)
+ false
+ }
+ }
+
+ val errors = found filter isReported
+
+ 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)
+ if (failOnProblem) s.log.error(p)
+ else s.log.warn(p)
}
- if(failOnProblem && !errors.isEmpty) sys.error(projectName + ": Binary compatibility check failed!")
+ if (failOnProblem && !errors.isEmpty) sys.error(projectName + ": Binary compatibility check failed!")
}
/** Resolves an artifact representing the previous abstract binary interface
- * for testing.
+ * for testing.
*/
def getPreviousArtifact(m: ModuleID, ivy: IvySbt, s: TaskStreams): File = {
val moduleSettings = InlineConfiguration(
"dummy" % "test" % "version",
ModuleInfo("dummy-test-project-for-resolving"),
- dependencies = Seq(m)
- )
+ dependencies = Seq(m))
val module = new ivy.Module(moduleSettings)
val report = IvyActions.update(
module,
new UpdateConfiguration(
- retrieve = None,
- missingOk = false,
- logging = UpdateLogging.DownloadOnly),
- s.log
- )
+ retrieve = None,
+ missingOk = false,
+ logging = UpdateLogging.DownloadOnly),
+ s.log)
val optFile = (for {
config <- report.configurations
module <- config.modules

0 comments on commit e5f84a4

Please sign in to comment.
Something went wrong with that request. Please try again.