diff --git a/buildSrc/src/main/java/com/instructure/android/buildtools/transform/ClassTransformer.kt b/buildSrc/src/main/java/com/instructure/android/buildtools/transform/ClassTransformer.kt index 181df7c919..1741b7fe9c 100644 --- a/buildSrc/src/main/java/com/instructure/android/buildtools/transform/ClassTransformer.kt +++ b/buildSrc/src/main/java/com/instructure/android/buildtools/transform/ClassTransformer.kt @@ -25,6 +25,12 @@ abstract class ClassTransformer { /** The name of this transformer */ abstract val transformName: String + /** + * Counts the number successful transforms performed by this Transformer and provides an assertion + * that this count matches a given condition. + */ + open val counter: TransformCounter = TransformCounter.Any + /** * Called once the [ClassPool] has been populated. At this point all source files have been scanned and it should be * safe to obtain [CtClass]es, add package imports, append the classpath, or otherwise interact with the [ClassPool]. @@ -47,6 +53,13 @@ abstract class ClassTransformer { */ abstract fun transform(cc: CtClass, classPool: ClassPool): Boolean + /** Invokes [transform] and updates the [counter] if the transform was successful */ + fun performTransform(cc: CtClass, classPool: ClassPool): Boolean { + val transformed = transform(cc, classPool) + if (transformed) counter.increment() + return transformed + } + val filter by lazy { createFilter() } open val includeExternalLibs: List = emptyList() @@ -75,3 +88,41 @@ abstract class ClassTransformer { } +sealed class TransformCounter { + internal var transformCount: Int = 0 + + abstract fun assertCount() + + fun increment() { + transformCount++ + } + + object Any : TransformCounter() { + override fun assertCount() = Unit + } + + class AtLest(private val atLeast: Int) : TransformCounter() { + override fun assertCount() { + if (transformCount < atLeast) { + throw IllegalStateException("Transformer ran $transformCount time(s) but was required to run at least $atLeast time(s)") + } + } + } + + class AtMost(private val atMost: Int) : TransformCounter() { + override fun assertCount() { + if (transformCount > atMost) { + throw IllegalStateException("Transformer ran $transformCount time(s) but was required to run at most $atMost time(s)") + } + } + } + + class Exactly(private val exactly: Int) : TransformCounter() { + override fun assertCount() { + if (transformCount != exactly) { + throw IllegalStateException("Transformer ran $transformCount time(s) but was required to run exactly $exactly time(s)") + } + } + } +} + diff --git a/buildSrc/src/main/java/com/instructure/android/buildtools/transform/PDFKitCrashFixTransformer.kt b/buildSrc/src/main/java/com/instructure/android/buildtools/transform/PDFKitCrashFixTransformer.kt index 6136c6d57c..91fdf2760b 100644 --- a/buildSrc/src/main/java/com/instructure/android/buildtools/transform/PDFKitCrashFixTransformer.kt +++ b/buildSrc/src/main/java/com/instructure/android/buildtools/transform/PDFKitCrashFixTransformer.kt @@ -25,7 +25,9 @@ class PDFKitCrashFixTransformer : ClassTransformer() { override val transformName = "PSPDFKitYUDoDis" - override val includeExternalLibs = listOf("pspdfkit-4.8.1.aar") + override val counter = TransformCounter.Exactly(1) + + override val includeExternalLibs = listOf("pspdfkit:4.8.1") private val problemClassName = "com.pspdfkit.ui.PdfThumbnailGrid" diff --git a/buildSrc/src/main/java/com/instructure/android/buildtools/transform/ProjectTransformer.kt b/buildSrc/src/main/java/com/instructure/android/buildtools/transform/ProjectTransformer.kt index baf8430714..1da5c20ebb 100644 --- a/buildSrc/src/main/java/com/instructure/android/buildtools/transform/ProjectTransformer.kt +++ b/buildSrc/src/main/java/com/instructure/android/buildtools/transform/ProjectTransformer.kt @@ -44,18 +44,19 @@ class ProjectTransformer( ) override fun transform(transformInvocation: TransformInvocation) = with(transformInvocation) { + val start = System.currentTimeMillis() // Don't transform anything if this is a test APK; just copy inputs and return if (context.variantName.endsWith("AndroidTest")) { - inputs.forEach { - it.jarInputs.forEach { + inputs.forEach { input -> + input.jarInputs.forEach { val dest = outputProvider.getContentLocation(it.name, it.contentTypes, it.scopes, Format.JAR) when (it.status) { Status.REMOVED -> dest.delete() else -> it.file.copyTo(dest, true) } } - it.directoryInputs.forEach { + input.directoryInputs.forEach { val dest = outputProvider.getContentLocation(it.name, it.contentTypes, it.scopes, Format.DIRECTORY) it.file.copyRecursively(dest, overwrite = true) } @@ -88,7 +89,7 @@ class ProjectTransformer( jarInput.file.copyTo(dest, true) classPool.insertClassPath(jarInput.file.absolutePath) if (jarInput.scopes.contains(QualifiedContent.Scope.SUB_PROJECTS) - || includedExternalLibs.any { it in jarInput.file.absolutePath }) { + || includedExternalLibs.any { it in jarInput.name }) { candidateJars += jarInput.file to dest } } @@ -112,7 +113,7 @@ class ProjectTransformer( // Transform files candidateFiles.forEach { (cc, src) -> try { - val modCount = transformers.count { it.filter.matches(cc) && it.transform(cc, classPool) } + val modCount = transformers.count { it.filter.matches(cc) && it.performTransform(cc, classPool) } if (modCount > 0) src.writeBytes(cc.toBytecode()) } catch (e: Throwable) { println("Error transforming file ${src.nameWithoutExtension}") @@ -130,7 +131,7 @@ class ProjectTransformer( if (entry.name.endsWith(".class")) { val cc = classPool[entry.name.substringBeforeLast(".class").replace("/", ".")] if (cc.isFrozen) cc.defrost() - if (transformers.count { it.filter.matches(cc) && it.transform(cc, classPool) } > 0) { + if (transformers.count { it.filter.matches(cc) && it.performTransform(cc, classPool) } > 0) { output.putNextEntry(JarEntry(entry.name)) output.write(cc.toBytecode()) } else { @@ -148,6 +149,12 @@ class ProjectTransformer( output.close() input.close() } + + // Assert the transform counts match their expected conditions + transformers.forEach { it.counter.assertCount() } + + val end = System.currentTimeMillis() + println(" :Transform took ${(end - start) / 1000.0} seconds") } /** Copies [entry] from the [input] jar to the [output] jar without modification */