Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run Mima in separate classloader #42

Merged
merged 4 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Port of the [MiMa Sbt Plugin](https://github.com/lightbend/mima)
After importing it in the `build.sc` file:

```scala
import $ivy.`com.github.lolgab::mill-mima_mill0.9:x.y.z`
import $ivy.`com.github.lolgab::mill-mima::x.y.z`
import com.github.lolgab.mill.mima._
```

Expand Down Expand Up @@ -40,8 +40,7 @@ you need to use the `mimaBinaryIssueFilters` setting to filter it out and get `m
pass, like so:

```scala
import import com.github.lolgab.mill.mima.{Mima, ProblemFilter}
import com.typesafe.tools.mima.core.{MissingClassProblem}
import com.github.lolgab.mill.mima._

object mylibrary extends ScalaModule with PublishModule with Mima {
override def mimaBinaryIssueFilters = super.mimaBinaryIssueFilters() ++ Seq(
Expand All @@ -55,8 +54,7 @@ object mylibrary extends ScalaModule with PublishModule with Mima {
You may also use wildcards in the package and/or the top `Problem` parent type for such situations:

```scala
import import com.github.lolgab.mill.mima.{Mima, ProblemFilter}
import com.typesafe.tools.mima.core.{MissingClassProblem}
import com.github.lolgab.mill.mima._

override def mimaBinaryIssueFilters = super.mimaBinaryIssueFilters() ++ Seq(
ProblemFilter.exclude[MissingClassProblem]("com.example.mylibrary.internal.*")
Expand All @@ -68,7 +66,7 @@ override def mimaBinaryIssueFilters = super.mimaBinaryIssueFilters() ++ Seq(
The fully-qualified class names of annotations that exclude parts of the API from problem checking.

```scala
import com.typesafe.tools.mima.core._
import com.github.lolgab.mill.mima._

object mylibrary extends ScalaModule with PublishModule with Mima {
override def mimaExcludeAnnotations = Seq(
Expand Down Expand Up @@ -102,7 +100,6 @@ Signature:
def mimaBackwardIssueFilters: Target[Map[String, Seq[ProblemFilter]]]
```


### mimaForwardIssueFilters

Filters to apply to binary issues found grouped by version of a module
Expand Down
59 changes: 37 additions & 22 deletions build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import mill._
import mill.scalalib._
import mill.scalalib.api.Util.scalaNativeBinaryVersion
import mill.scalalib.publish._
import $ivy.`com.lihaoyi::mill-contrib-buildinfo:`
import mill.contrib.buildinfo.BuildInfo
import $ivy.`com.lihaoyi::mill-contrib-bloop:$MILL_VERSION`
import $ivy.`de.tototec::de.tobiasroeser.mill.integrationtest::0.5.0`
import de.tobiasroeser.mill.integrationtest._
Expand All @@ -14,7 +16,7 @@ import $ivy.`com.github.lolgab::mill-mima::0.0.9`
import com.github.lolgab.mill.mima._
import os.Path

val millVersions = Seq("0.9.3", "0.10.0")
val millVersions = Seq("0.9.7", "0.10.0")
val millBinaryVersions = millVersions.map(scalaNativeBinaryVersion)

def millBinaryVersion(millVersion: String) = scalaNativeBinaryVersion(
Expand All @@ -23,18 +25,7 @@ def millBinaryVersion(millVersion: String) = scalaNativeBinaryVersion(
def millVersion(binaryVersion: String) =
millVersions.find(v => millBinaryVersion(v) == binaryVersion).get

object `mill-mima` extends Cross[MillMimaCross](millBinaryVersions: _*)
class MillMimaCross(millBinaryVersion: String)
extends ScalaModule
with PublishModule
with ScalafixModule
with Mima {
def mimaPreviousArtifacts = Agg(
ivy"com.github.lolgab::mima_mill$millBinaryVersion:0.0.1"
)
override def millSourcePath = super.millSourcePath / os.up
override def artifactName = s"mill-mima_mill$millBinaryVersion"

trait Common extends ScalaModule with PublishModule with ScalafixModule {
def pomSettings = PomSettings(
description = "MiMa Mill Plugin",
organization = "com.github.lolgab",
Expand All @@ -45,32 +36,56 @@ class MillMimaCross(millBinaryVersion: String)
Developer("lolgab", "Lorenzo Gabriele", "https://github.com/lolgab")
)
)
def publishVersion = VcsVersion.vcsState().format()
def scalaVersion = "2.13.8"

def scalacOptions =
super.scalacOptions() ++ Seq("-Ywarn-unused", "-deprecation")

def scalafixIvyDeps = Agg(ivy"com.github.liancheng::organize-imports:0.6.0")
}

object `mill-mima` extends Cross[MillMimaCross](millBinaryVersions: _*)
class MillMimaCross(val millBinaryVersion: String) extends Common with BuildInfo with Mima {
override def moduleDeps = super.moduleDeps ++ Seq(`mill-mima-worker-api`)
override def artifactName = s"mill-mima_mill$millBinaryVersion"
override def millSourcePath = super.millSourcePath / os.up
def mimaPreviousArtifacts = Agg(
ivy"com.github.lolgab::mima_mill$millBinaryVersion:0.0.1"
)
override def sources = T.sources(
super.sources() ++ Seq(millSourcePath / s"src-mill$millBinaryVersion").map(PathRef(_))
)
def publishVersion = VcsVersion.vcsState().format()
def scalaVersion = "2.13.4"
override def compileIvyDeps = super.compileIvyDeps() ++ Agg(
ivy"com.lihaoyi::mill-scalalib:${millVersion(millBinaryVersion)}"
)
def ivyDeps = super.ivyDeps() ++ Agg(
ivy"com.typesafe::mima-core:1.0.1"
override def buildInfoMembers = Map(
"publishVersion" -> publishVersion()
)
override def buildInfoObjectName = "MimaBuildInfo"
override def buildInfoPackageName = Some("com.github.lolgab.mill.mima.worker")
}

def scalacOptions =
super.scalacOptions() ++ Seq("-Ywarn-unused", "-deprecation")

def scalafixIvyDeps = Agg(ivy"com.github.liancheng::organize-imports:0.4.4")
object `mill-mima-worker-api` extends Common
object `mill-mima-worker-impl` extends Common {
override def moduleDeps = super.moduleDeps ++ Seq(`mill-mima-worker-api`)
override def ivyDeps = super.ivyDeps() ++ Agg(
ivy"com.typesafe::mima-core:1.0.1"
)
}

object itest extends Cross[itestCross](
"0.9.3", "0.9.7", "0.9.8", "0.9.11",
"0.9.7", "0.9.8", "0.9.11",
"0.10.0"
)
class itestCross(millVersion: String) extends MillIntegrationTestModule {
override def millSourcePath: Path = super.millSourcePath / os.up
def millTestVersion = millVersion
def pluginsUnderTest = Seq(`mill-mima`(millBinaryVersion(millVersion)))
def temporaryIvyModules = Seq(
`mill-mima-worker-impl`,
`mill-mima-worker-api`
)
def testBase = millSourcePath / "src"
override def testInvocations: T[Seq[(PathRef, Seq[TestInvocation.Targets])]] =
T {
Expand Down
1 change: 0 additions & 1 deletion itest/src/filters/build.sc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import mill._
import mill.scalalib._
import mill.scalalib.publish._
import com.github.lolgab.mill.mima._
import com.typesafe.tools.mima.core.Problem

trait Common extends ScalaModule with PublishModule {
def scalaVersion = "2.13.4"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.lolgab.mill.mima.worker.api;

public class Artifact {
public String prettyDep;
public java.io.File file;

public Artifact(String prettyDep, java.io.File file) {
this.prettyDep = prettyDep;
this.file = file;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.lolgab.mill.mima.worker.api;

public enum CheckDirection {
Backward, Forward, Both
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.lolgab.mill.mima.worker.api;

public interface MimaWorkerApi {
java.util.Optional<String> reportBinaryIssues(
String scalaBinaryVersion,
java.util.function.Consumer<String> logDebug,
java.util.function.Consumer<String> logError,
java.util.function.Consumer<String> logPrintln,
CheckDirection checkDirection,
java.io.File[] runClasspath,
Artifact[] previous,
java.io.File current,
ProblemFilter[] binaryFilters,
java.util.Map<String, ProblemFilter[]> backwardFilters,
java.util.Map<String, ProblemFilter[]> forwardFilters,
String[] excludeAnnos,
String publishVersion
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.lolgab.mill.mima.worker.api;

public final class ProblemFilter {
public final String name;
public final String problem;

public ProblemFilter(String n, String p) {
name = n;
problem = p;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.github.lolgab.mill.mima.worker

import com.github.lolgab.mill.mima.worker.api._
import com.typesafe.tools.mima.core.MyProblemReporting
import com.typesafe.tools.mima.core.Problem
import com.typesafe.tools.mima.core.ProblemFilters
import com.typesafe.tools.mima.core.{ProblemFilter => MimaProblemFilter}
import com.typesafe.tools.mima.lib.MiMaLib

import scala.jdk.CollectionConverters._

class MimaWorkerImpl extends MimaWorkerApi {

def reportBinaryIssues(
scalaBinaryVersion: String,
logDebug: java.util.function.Consumer[String],
logError: java.util.function.Consumer[String],
logPrintln: java.util.function.Consumer[String],
checkDirection: CheckDirection,
runClasspath: Array[java.io.File],
previous: Array[Artifact],
current: java.io.File,
binaryFilters: Array[ProblemFilter],
backwardFilters: java.util.Map[String, Array[ProblemFilter]],
forwardFilters: java.util.Map[String, Array[ProblemFilter]],
excludeAnnos: Array[String],
publishVersion: String
): java.util.Optional[String] = {
sanityCheckScalaBinaryVersion(scalaBinaryVersion)

val mimaLib = new MiMaLib(runClasspath.toSeq)

def isReported(
versionedFilters: java.util.Map[String, Array[ProblemFilter]]
)(problem: Problem) = {
val filters = binaryFilters.map(problemFilterToMima).toSeq
val mimaVersionedFilters = versionedFilters.asScala.map { case (k, v) =>
k -> v.map(problemFilterToMima).toSeq
}.toMap
MyProblemReporting.isReported(
publishVersion,
filters,
mimaVersionedFilters
)(problem)
}

logPrintln.accept(
s"Scanning binary compatibility in ${current} ..."
)
val (problemsCount, filteredCount) =
previous.foldLeft((0, 0)) { case ((totalAgg, filteredAgg), prev) =>
def checkBC =
mimaLib.collectProblems(prev.file, current, excludeAnnos.toList)

def checkFC =
mimaLib.collectProblems(current, prev.file, excludeAnnos.toList)

val (backward, forward) = checkDirection match {
case CheckDirection.Backward => (checkBC, Nil)
case CheckDirection.Forward => (Nil, checkFC)
case CheckDirection.Both => (checkBC, checkFC)
}
val backErrors = backward.filter(isReported(backwardFilters))
val forwErrors = forward.filter(isReported(forwardFilters))
val count = backErrors.size + forwErrors.size
val filteredCount = backward.size + forward.size - count
val doLog = if (count == 0) logDebug.accept(_) else logError.accept(_)
doLog(s"Found ${count} issue when checking against ${prev.prettyDep}")
backErrors.foreach(problem => doLog(prettyProblem("current")(problem)))
forwErrors.foreach(problem => doLog(prettyProblem("other")(problem)))
(totalAgg + count, filteredAgg + filteredCount)
}

if (problemsCount > 0) {
val filteredNote =
if (filteredCount > 0) s" (filtered $filteredCount)" else ""
java.util.Optional.of(
s"Failed binary compatibility check! Found $problemsCount potential problems$filteredNote"
)
} else {
logPrintln.accept("Binary compatibility check passed")
java.util.Optional.empty()
}
}

private def prettyProblem(affected: String)(p: Problem): String = {
val desc = p.description(affected)
val howToFilter = p.howToFilter.fold("")(s =>
s"\n filter with: ${s.replace("ProblemFilters.exclude", ("ProblemFilter.exclude"))}"
)
s" * $desc$howToFilter"
}

private def sanityCheckScalaBinaryVersion(scalaBinaryVersion: String) = {
scalaBinaryVersion match {
case "2.11" | "2.12" | "2.13" | "3" => // ok
case _ =>
throw new IllegalArgumentException(
s"MiMa supports Scala 2.11, 2.12, 2.13 and 3, not $scalaBinaryVersion"
)
}
}

private def problemFilterToMima(
problemFilter: ProblemFilter
): MimaProblemFilter =
ProblemFilters.exclude(problemFilter.problem, problemFilter.name)

}
Loading