in buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy [19:160]
void apply(Project project) {
project.extensions.create("checkApiChanges", CheckApiChangesExtension)
project.configurations {
checkApiChangesFrom
checkApiChangesTo
}
project.afterEvaluate {
project.checkApiChanges.from.each {
project.dependencies.checkApiChangesFrom(it) {
transitive = false
force = true
}
}
project.checkApiChanges.to.findAll { it instanceof String }.each {
project.dependencies.checkApiChangesTo(it) {
transitive = false
force = true
}
}
}
project.task('checkForApiChanges', dependsOn: 'jar') {
doLast {
Map<ClassMethod, Change> changedClassMethods = new TreeMap<>()
def fromUrls = project.configurations.checkApiChangesFrom*.toURI()*.toURL()
println "fromUrls = ${fromUrls*.toString()*.replaceAll("^.*/", "")}"
def jarUrls = project.checkApiChanges.to
.findAll { it instanceof Project }
.collect { it.jar.archivePath.toURL() }
def toUrls = jarUrls + project.configurations.checkApiChangesTo*.toURI()*.toURL()
println "toUrls = ${toUrls*.toString()*.replaceAll("^.*/", "")}"
Analysis prev = new Analysis(fromUrls)
Analysis cur = new Analysis(toUrls)
Set<String> allMethods = new TreeSet<>(prev.classMethods.keySet())
allMethods.addAll(cur.classMethods.keySet())
Set<ClassMethod> deprecatedNotRemoved = new TreeSet<>()
Set<ClassMethod> newlyDeprecated = new TreeSet<>()
for (String classMethodName : allMethods) {
ClassMethod prevClassMethod = prev.classMethods.get(classMethodName)
ClassMethod curClassMethod = cur.classMethods.get(classMethodName)
if (prevClassMethod == null) {
// added
if (curClassMethod.visible) {
changedClassMethods.put(curClassMethod, Change.ADDED)
}
} else if (curClassMethod == null) {
def theClass = prevClassMethod.classNode.name.replace('/', '.')
def methodDesc = prevClassMethod.methodDesc
while (curClassMethod == null && cur.parents[theClass] != null) {
theClass = cur.parents[theClass]
def parentMethodName = "${theClass}#${methodDesc}"
curClassMethod = cur.classMethods[parentMethodName]
}
// removed
if (curClassMethod == null && prevClassMethod.visible && !prevClassMethod.deprecated) {
if (classMethodName.contains("getActivityTitle")) {
println "hi!"
}
changedClassMethods.put(prevClassMethod, Change.REMOVED)
}
} else {
if (prevClassMethod.deprecated) {
deprecatedNotRemoved << prevClassMethod;
} else if (curClassMethod.deprecated) {
newlyDeprecated << prevClassMethod;
}
// println "changed: $classMethodName"
}
}
String prevClassName = null
def introClass = { classMethod ->
if (classMethod.className != prevClassName) {
prevClassName = classMethod.className
println "\n$prevClassName:"
}
}
def entryPoints = project.checkApiChanges.entryPoints
Closure matchesEntryPoint = { ClassMethod classMethod ->
for (String entryPoint : entryPoints) {
if (classMethod.className.matches(entryPoint)) {
return true
}
}
return false
}
def expectedREs = project.checkApiChanges.expectedChanges.collect { Pattern.compile(it) }
for (Map.Entry<ClassMethod, Change> change : changedClassMethods.entrySet()) {
def classMethod = change.key
def changeType = change.value
def showAllChanges = true // todo: only show stuff that's interesting...
if (matchesEntryPoint(classMethod) || showAllChanges) {
String classMethodDesc = classMethod.desc
def expected = expectedREs.any { it.matcher(classMethodDesc).find() }
if (!expected) {
introClass(classMethod)
switch (changeType) {
case Change.ADDED:
println "+ ${classMethod.methodDesc}"
break
case Change.REMOVED:
println "- ${classMethod.methodDesc}"
break
}
}
}
}
if (!deprecatedNotRemoved.empty) {
println "\nDeprecated but not removed:"
for (ClassMethod classMethod : deprecatedNotRemoved) {
introClass(classMethod)
println "* ${classMethod.methodDesc}"
}
}
if (!newlyDeprecated.empty) {
println "\nNewly deprecated:"
for (ClassMethod classMethod : newlyDeprecated) {
introClass(classMethod)
println "* ${classMethod.methodDesc}"
}
}
}
}
}