aboutsummaryrefslogtreecommitdiff
path: root/buildSrc
diff options
context:
space:
mode:
authorChristian Williams <christianw@google.com>2016-12-21 17:38:44 -0800
committerChristian Williams <christianw@google.com>2016-12-21 17:38:44 -0800
commit5c292730b8ae2ea89bf60e682e299fbe4a99ef6c (patch)
tree60abb84e4284cf4371f52d1bd60a8ae9ca94f2f4 /buildSrc
parentb724e4323ccbc4bd96a501b80faf24295e9e7e1e (diff)
downloadrobolectric-shadows-5c292730b8ae2ea89bf60e682e299fbe4a99ef6c.tar.gz
WIP
Diffstat (limited to 'buildSrc')
-rw-r--r--buildSrc/build.gradle2
-rw-r--r--buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy218
-rw-r--r--buildSrc/src/main/groovy/ProvideBuildClasspathTask.groovy2
3 files changed, 221 insertions, 1 deletions
diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle
index 0da01d240..c634e0431 100644
--- a/buildSrc/build.gradle
+++ b/buildSrc/build.gradle
@@ -8,4 +8,6 @@ repositories {
dependencies {
compile gradleApi()
compile localGroovy()
+
+ compile "org.ow2.asm:asm-tree:5.0.1"
}
diff --git a/buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy b/buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy
new file mode 100644
index 000000000..8a392bb96
--- /dev/null
+++ b/buildSrc/src/main/groovy/CheckApiChangesPlugin.groovy
@@ -0,0 +1,218 @@
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.tree.AnnotationNode
+import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.tree.MethodNode
+
+import java.util.jar.JarEntry
+import java.util.jar.JarInputStream
+import java.util.regex.Pattern
+
+import static java.util.Arrays.asList
+import static org.objectweb.asm.Opcodes.ACC_PRIVATE
+
+class CheckApiChangesPlugin implements Plugin<Project> {
+ @Override
+ void apply(Project project) {
+// project.extensions.create("checkApiChanges", CheckApiChangesExtension)
+
+ project.configurations {
+ checkApiChanges
+ }
+
+// project.afterEvaluate {
+// project.dependencies.checkApiChanges project.checkApiChanges.baseArtifact
+// }
+
+ project.task('checkForApiChanges', dependsOn: 'jar') {
+ doLast {
+
+ def baseUrls = project.configurations.checkApiChanges*.toURI()*.toURL()
+ println "${project.name}: checkForApiChanges: ${baseUrls}"
+ Map<String, ClassMethod> prevClassMethods = findClassMethods(baseUrls)
+ Map<String, ClassMethod> curClassMethods = findClassMethods(asList(new URL("file://${project.jar.archivePath}")))
+ Set<String> allMethods = new TreeSet<>(prevClassMethods.keySet())
+ allMethods.addAll(curClassMethods.keySet())
+ String prevClassName = null
+ for (String classMethodName : allMethods) {
+ ClassMethod prevClassMethod = prevClassMethods.get(classMethodName)
+ ClassMethod curClassMethod = curClassMethods.get(classMethodName)
+
+ def introClass = { classMethod ->
+ if (classMethod.className != prevClassName) {
+ prevClassName = classMethod.className
+ println "\n$prevClassName:"
+ }
+ }
+
+ if (prevClassMethod == null) {
+ // added
+ if (curClassMethod.visible) {
+ introClass(curClassMethod)
+ println "+ $curClassMethod.methodDesc"
+ }
+ } else if (curClassMethod == null) {
+ // removed
+ if (prevClassMethod.visible && !prevClassMethod.deprecated) {
+ introClass(prevClassMethod)
+ println "- $prevClassMethod.methodDesc (not previously @Deprecated)" }
+
+ } else {
+// println "changed: $classMethodName"
+ }
+ }
+
+ println "it = ${}"
+// println prevClassMethods
+ }
+ }
+ }
+
+ private Map<String, ClassMethod> findClassMethods(List<URL> baseUrls) {
+ Map<String, ClassMethod> classMethods = new HashMap<>()
+ for (URL url : baseUrls) {
+ if (url.protocol == 'file') {
+ def file = new File(url.path)
+ println "file = ${file}"
+ def stream = new FileInputStream(file)
+ def jarStream = new JarInputStream(stream)
+ while (true) {
+ JarEntry entry = jarStream.nextJarEntry
+ if (entry == null) break
+
+ if (!entry.directory && entry.name.endsWith(".class")) {
+ def reader = new ClassReader(jarStream)
+ def node = new ClassNode()
+ reader.accept(node, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES)
+// println "node = $node.name"
+ for (MethodNode method : node.methods) {
+ def classMethod = new ClassMethod(node, method)
+ classMethods.put(classMethod.desc, classMethod)
+ if (method.access != ACC_PRIVATE) {
+// println "method.name = $method.name ${method.signature}"
+ for (AnnotationNode annotationNode : method.visibleAnnotations) {
+ println "annotationNode = $annotationNode.desc"
+ }
+ }
+ }
+ }
+ }
+ stream.close()
+ }
+ }
+ classMethods
+ }
+
+ static class ClassMethod {
+ ClassNode classNode
+ MethodNode methodNode
+
+ ClassMethod(ClassNode classNode, MethodNode methodNode) {
+ this.classNode = classNode
+ this.methodNode = methodNode
+ }
+
+ boolean equals(o) {
+ if (this.is(o)) return true
+ if (getClass() != o.class) return false
+
+ ClassMethod that = (ClassMethod) o
+
+ if (classNode.name != that.classNode.name) return false
+ if (methodNode.name != that.methodNode.name) return false
+ if (methodNode.signature != that.methodNode.signature) return false
+
+ return true
+ }
+
+ int hashCode() {
+ int result
+ result = (classNode.name != null ? classNode.name.hashCode() : 0)
+ result = 31 * result + (methodNode.name != null ? methodNode.name.hashCode() : 0)
+ result = 31 * result + (methodNode.signature != null ? methodNode.signature.hashCode() : 0)
+ return result
+ }
+
+ public String getDesc() {
+ return "$className#$methodDesc"
+ }
+
+ private String getMethodDesc() {
+ def args = new StringBuilder()
+ def returnType = new StringBuilder()
+ def buf = args
+
+ int arrayDepth = 0
+ def write = { typeName ->
+ if (buf.size() > 0) buf.append(", ")
+ buf.append(typeName)
+ for (; arrayDepth > 0; arrayDepth--) {
+ buf.append("[]")
+ }
+ }
+
+ def chars = methodNode.desc.toCharArray()
+ def i = 0
+
+ def readObj = {
+ if (buf.size() > 0) buf.append(", ")
+ for (; i < chars.length; i++) {
+ char c = chars[i]
+ if (c == ';' as char) break
+ buf.append((c == '/' as char) ? '.' : c)
+ }
+ }
+
+ for (; i < chars.length;) {
+ def c = chars[i++]
+ switch (c) {
+ case '(': break;
+ case ')': buf = returnType; break;
+ case '[': arrayDepth++; break;
+ case 'Z': write('boolean'); break;
+ case 'B': write('byte'); break;
+ case 'S': write('short'); break;
+ case 'I': write('int'); break;
+ case 'J': write('long'); break;
+ case 'F': write('float'); break;
+ case 'D': write('double'); break;
+ case 'C': write('char'); break;
+ case 'L': readObj(); break;
+ case 'V': write('void'); break;
+ }
+ }
+ "${returnType.toString()} $methodNode.name(${args.toString()})"
+ }
+
+ @Override
+ public String toString() {
+ return className + "#$methodNode.desc";
+ }
+
+ private String getSignature() {
+ methodNode.signature == null ? "()V" : methodNode.signature
+ }
+
+ private String getClassName() {
+ classNode.name.replace('/', '.')
+ }
+
+ boolean isDeprecated() {
+ for (AnnotationNode annotationNode : methodNode.visibleAnnotations) {
+ if (annotationNode.desc == "Ljava/lang/Deprecated;") {
+ return true
+ }
+ }
+ false
+ }
+
+ boolean isVisible() {
+ classNode.access != ACC_PRIVATE && !(classNode.name =~ /\$[0-9]/) && !(methodNode.name =~ /^access\$/)
+ }
+ }
+}
+
+class CheckApiChangesExtension {
+ String baseArtifact
+} \ No newline at end of file
diff --git a/buildSrc/src/main/groovy/ProvideBuildClasspathTask.groovy b/buildSrc/src/main/groovy/ProvideBuildClasspathTask.groovy
index c329f0d64..81e21d4b5 100644
--- a/buildSrc/src/main/groovy/ProvideBuildClasspathTask.groovy
+++ b/buildSrc/src/main/groovy/ProvideBuildClasspathTask.groovy
@@ -12,7 +12,7 @@ class ProvideBuildClasspathTask extends DefaultTask {
project.rootProject.allprojects.each { Project otherProject ->
def match = otherProject.name =~ /robolectric-shadows\/(.*)/
if (match.matches()) {
- def artifactName = "${otherProject.group}:${otherProject.mavenArtifactName()}:${otherProject.version}"
+ def artifactName = "${otherProject.group}:${otherProject.mavenArtifactName}:${otherProject.version}"
File classesDir = otherProject.sourceSets['main'].output.classesDir
File resourcesDir = otherProject.sourceSets['main'].output.resourcesDir
paths << "${artifactName.replaceAll(/:/, '\\\\:')}: ${classesDir}:${resourcesDir}"