Development tool for generating MIMA exclusion files to support binary compatibility checking in Apache Spark builds
npx @tessl/cli install tessl/maven-org-apache-spark--spark-tools-2-12@3.0.0Spark Tools is a development utility for Apache Spark that generates MIMA (Migration Manager for Scala) exclusion files. It analyzes compiled Spark classes to identify package-private APIs that should be excluded from binary compatibility checks, supporting Spark's release engineering process.
org.apache.spark:spark-tools_2.12:3.0.1import org.apache.spark.tools.GenerateMIMAIgnore
// For direct API usage (advanced scenarios)
import scala.reflect.runtime.{universe => unv}
import scala.reflect.runtime.universe.runtimeMirror
import org.clapper.classutil.ClassFinderThis tool is designed to be executed via Apache Spark's spark-class script:
./spark-class org.apache.spark.tools.GenerateMIMAIgnoreThe tool will:
org.apache.spark package.generated-mima-class-excludes.generated-mima-member-excludesThe tool operates through Scala reflection to analyze compiled bytecode:
org.clapper.classutil.ClassFinder to locate all Spark classes on the classpathscala.reflect.runtime.universe) to examine visibility modifiers and package privacy$$ patterns)scala.util.Tryorg.apache.spark package using ClassFinderprivate[spark] annotations) and indirect privacy (nested within private classes)Executes the complete MIMA exclusion generation process for Apache Spark classes.
def main(args: Array[String]): UnitParameters:
args: Array[String] - Command line arguments (currently unused)Side Effects:
.generated-mima-class-excludes file containing class exclusion patterns.generated-mima-member-excludes file containing member exclusion patternsUsage Example:
// Typically invoked via spark-class script
object MyApp {
def main(args: Array[String]): Unit = {
org.apache.spark.tools.GenerateMIMAIgnore.main(Array.empty)
}
}Analyzes all classes in a given package to identify package-private classes and members that should be excluded from MIMA binary compatibility checks.
def privateWithin(packageName: String): (Set[String], Set[String])Parameters:
packageName: String - The package name to analyze (typically "org.apache.spark")Returns:
(Set[String], Set[String]) - Tuple containing:
Usage Example:
val (privateClasses, privateMembers) = GenerateMIMAIgnore.privateWithin("org.apache.spark")
// privateClasses contains: Set("org.apache.spark.internal.SomeClass", "org.apache.spark.internal.SomeClass#")
// privateMembers contains: Set("org.apache.spark.SomeClass.privateMethod", ...)Scans all classes accessible from the context class loader which belong to the given package and subpackages, filtering out JVM-generated artifacts.
def getClasses(packageName: String): Set[String]Parameters:
packageName: String - The package name to scan for classesReturns:
Set[String] - Set of fully qualified class names found in the packageImplementation Details:
org.clapper.classutil.ClassFinder for efficient class discoveryshouldExclude to remove JVM-generated artifacts:
Extracts inner functions from a class using Java reflection, identifying methods with $$ patterns that Scala generates for inner functions.
def getInnerFunctions(classSymbol: unv.ClassSymbol): Seq[String]Parameters:
classSymbol: unv.ClassSymbol - Scala reflection symbol representing the class to analyzeReturns:
Seq[String] - Sequence of fully qualified inner function names found in the classImplementation Details:
$$ which indicate Scala compiler-generated functionsUsage Example:
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{universe => unv}
val mirror = runtimeMirror(getClass.getClassLoader)
val classSymbol = mirror.classSymbol(classOf[SomeSparkClass])
val innerFunctions = GenerateMIMAIgnore.getInnerFunctions(classSymbol)
// Returns: Seq("com.example.SomeSparkClass.$$anonfun$method$1", ...)// Scala reflection universe import alias
import scala.reflect.runtime.{universe => unv}
// Core Scala reflection types used by the API
type ClassSymbol = scala.reflect.runtime.universe.ClassSymbol
type ModuleSymbol = scala.reflect.runtime.universe.ModuleSymbol
type Symbol = scala.reflect.runtime.universe.Symbol
type RuntimeMirror = scala.reflect.runtime.universe.Mirror
// ClassFinder from external library
type ClassFinder = org.clapper.classutil.ClassFinder
// Scala compiler file I/O utilities
type File = scala.tools.nsc.io.FileThe tool requires these runtime dependencies:
scala-reflect - Scala reflection APIscala-compiler - Scala compiler utilities for file I/Oorg.clapper.classutil - Third-party library for class discoveryThe tool includes comprehensive defensive error handling:
ClassNotFoundException and reflection failures"Error instrumenting class:" + className"[WARN] Unable to detect inner functions for class:" + classSymbol.fullNamescala.util.Try for reading existing exclusion files to handle cases where files don't existTry(File(".generated-mima-class-excludes").lines()).getOrElse(Iterator.empty)The tool generates two exclusion files with specific formatting patterns:
.generated-mima-class-excludesContains package-private class exclusions with MIMA-compatible patterns:
$ replaced by # for Scala objectsorg.apache.spark.internal.SomePrivateClass
org.apache.spark.internal.SomePrivateClass#
org.apache.spark.scheduler.cluster.mesos.MesosTaskLaunchData
org.apache.spark.scheduler.cluster.mesos.MesosTaskLaunchData#.generated-mima-member-excludesContains package-private member exclusions including methods, fields, and inner functions:
$$ in the nameorg.apache.spark.SomeClass.privateMethod
org.apache.spark.SomeClass.privateField
org.apache.spark.SomeClass.$$anonfun$someMethod$1
org.apache.spark.util.Utils.$$anonfun$tryOrIOException$1scala.util.Try