Skip to content

Commit

Permalink
Merge pull request #12 from havocp/havocp-filter-problems
Browse files Browse the repository at this point in the history
mima-binary-issue-filters key
  • Loading branch information
jsuereth committed Jun 6, 2012
2 parents 8f76fbc + 34d8b4f commit e5f84a4
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 34 deletions.
17 changes: 17 additions & 0 deletions 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)
}
}
18 changes: 15 additions & 3 deletions core/src/main/scala/com/typesafe/tools/mima/core/Problems.scala
Expand Up @@ -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 {
Expand All @@ -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"
Expand Down Expand Up @@ -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"
}
}
@@ -0,0 +1,5 @@
package com.typesafe.tools.mima

package object core {
type ProblemFilter = (Problem) => Boolean
}
Expand Up @@ -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.")
}
Expand Up @@ -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,
Expand All @@ -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
}
Expand Up @@ -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
Expand Down

0 comments on commit e5f84a4

Please sign in to comment.