diff --git a/src/functionalTest/groovy/com/autonomousapps/AbstractFunctionalSpec.groovy b/src/functionalTest/groovy/com/autonomousapps/AbstractFunctionalSpec.groovy index 60f321507..aa6f504fc 100644 --- a/src/functionalTest/groovy/com/autonomousapps/AbstractFunctionalSpec.groovy +++ b/src/functionalTest/groovy/com/autonomousapps/AbstractFunctionalSpec.groovy @@ -39,15 +39,15 @@ abstract class AbstractFunctionalSpec extends Specification { */ private static boolean isCi = System.getenv("CI") == "true" - def cleanup() { - // Delete fixtures on CI to prevent disk space growing out of bounds - if (gradleProject != null && isCi) { - try { - gradleProject.rootDir.deleteDir() - } catch (Throwable t) { - } - } - } +// def cleanup() { +// // Delete fixtures on CI to prevent disk space growing out of bounds +// if (gradleProject != null && isCi) { +// try { +// gradleProject.rootDir.deleteDir() +// } catch (Throwable t) { +// } +// } +// } protected static Boolean quick() { return System.getProperty('com.autonomousapps.quick').toBoolean() diff --git a/src/functionalTest/groovy/com/autonomousapps/jvm/DuplicateClasspathSpec.groovy b/src/functionalTest/groovy/com/autonomousapps/jvm/DuplicateClasspathSpec.groovy index c36fe2558..bf831e43f 100644 --- a/src/functionalTest/groovy/com/autonomousapps/jvm/DuplicateClasspathSpec.groovy +++ b/src/functionalTest/groovy/com/autonomousapps/jvm/DuplicateClasspathSpec.groovy @@ -3,6 +3,8 @@ package com.autonomousapps.jvm import com.autonomousapps.jvm.projects.BinaryIncompatibilityProject import com.autonomousapps.jvm.projects.DuplicateClasspathProject import com.autonomousapps.utils.Colors +import org.gradle.testkit.runner.TaskOutcome +import spock.lang.PendingFeature import static com.autonomousapps.advice.truth.BuildHealthSubject.buildHealth import static com.autonomousapps.utils.Runner.build @@ -20,17 +22,17 @@ final class DuplicateClasspathSpec extends AbstractJvmSpec { when: // This first invocation fixes the dependency declarations build(gradleVersion, gradleProject.rootDir, ':consumer:fixDependencies') - // This used to fail because of classpath duplication and the wrong dep getting loaded first. Recent improvements - // have improved this case, however. - build(gradleVersion, gradleProject.rootDir, 'buildHealth') + // This fails because of classpath duplication and the wrong dep getting loaded first + def result = buildAndFail(gradleVersion, gradleProject.rootDir, 'buildHealth') then: - assertThat(gradleProject.rootDir.toPath().resolve('consumer/build.gradle').text).contains( - '''\ - dependencies { - implementation project(':producer-2') - }'''.stripIndent() - ) + assertThat(result.task(':consumer:compileJava').outcome).isEqualTo(TaskOutcome.FAILED) +// assertThat(gradleProject.rootDir.toPath().resolve('consumer/build.gradle').text).contains( +// '''\ +// dependencies { +// implementation project(':producer-2') +// }'''.stripIndent() +// ) where: gradleVersion << [GRADLE_LATEST] @@ -69,6 +71,7 @@ final class DuplicateClasspathSpec extends AbstractJvmSpec { gradleVersion << [GRADLE_LATEST] } + @PendingFeature(reason = "This feature was reverted") def "can report on which of the duplicates is needed for binary compatibility (#gradleVersion)"() { given: def project = new BinaryIncompatibilityProject() @@ -123,6 +126,7 @@ final class DuplicateClasspathSpec extends AbstractJvmSpec { gradleVersion << [GRADLE_LATEST] } + @PendingFeature(reason = "This feature was reverted") def "suggests removing a binary-incompatible duplicate (#gradleVersion)"() { given: def project = new BinaryIncompatibilityProject(true) diff --git a/src/functionalTest/groovy/com/autonomousapps/jvm/projects/DuplicateClasspathProject.groovy b/src/functionalTest/groovy/com/autonomousapps/jvm/projects/DuplicateClasspathProject.groovy index c77615c33..bcb761784 100644 --- a/src/functionalTest/groovy/com/autonomousapps/jvm/projects/DuplicateClasspathProject.groovy +++ b/src/functionalTest/groovy/com/autonomousapps/jvm/projects/DuplicateClasspathProject.groovy @@ -176,6 +176,9 @@ final class DuplicateClasspathProject extends AbstractProject { private final Set consumerAdvice = [ Advice.ofRemove(projectCoordinates(':unused'), 'implementation'), + // This is actually bad advice, but we can't detect it without re-reverting the change to detect binary + // incompatibilities. + Advice.ofAdd(projectCoordinates(':producer-1'), 'implementation') ] final Set expectedProjectAdvice = [ diff --git a/src/main/kotlin/com/autonomousapps/internal/asm.kt b/src/main/kotlin/com/autonomousapps/internal/asm.kt index d78352228..54445bb97 100644 --- a/src/main/kotlin/com/autonomousapps/internal/asm.kt +++ b/src/main/kotlin/com/autonomousapps/internal/asm.kt @@ -9,7 +9,6 @@ import com.autonomousapps.internal.kotlin.AccessFlags import com.autonomousapps.internal.utils.METHOD_DESCRIPTOR_REGEX import com.autonomousapps.internal.utils.efficient import com.autonomousapps.internal.utils.genericTypes -import com.autonomousapps.model.internal.intermediates.producer.Member import com.autonomousapps.model.internal.intermediates.consumer.MemberAccess import kotlinx.metadata.jvm.Metadata import org.gradle.api.logging.Logger @@ -26,13 +25,14 @@ internal class ClassNameAndAnnotationsVisitor(private val logger: Logger) : Clas private lateinit var access: Access private var outerClassName: String? = null private var superClassName: String? = null - private var interfaces: Set? = null + + // private var interfaces: Set? = null private val retentionPolicyHolder = AtomicReference("") private var isAnnotation = false private val methods = mutableSetOf() private val innerClasses = mutableSetOf() - private val effectivelyPublicFields = mutableSetOf() - private val effectivelyPublicMethods = mutableSetOf() + // private val effectivelyPublicFields = mutableSetOf() + // private val effectivelyPublicMethods = mutableSetOf() private var methodCount = 0 private var fieldCount = 0 @@ -49,7 +49,7 @@ internal class ClassNameAndAnnotationsVisitor(private val logger: Logger) : Clas className = className, outerClassName = outerClassName, superClassName = superClassName!!, - interfaces = interfaces.orEmpty(), + // interfaces = interfaces.orEmpty(), retentionPolicy = retentionPolicyHolder.get(), isAnnotation = isAnnotation, hasNoMembers = hasNoMembers, @@ -57,8 +57,8 @@ internal class ClassNameAndAnnotationsVisitor(private val logger: Logger) : Clas methods = methods.efficient(), innerClasses = innerClasses.efficient(), constantClasses = constantClasses.efficient(), - effectivelyPublicFields = effectivelyPublicFields, - effectivelyPublicMethods = effectivelyPublicMethods, + // effectivelyPublicFields = effectivelyPublicFields, + // effectivelyPublicMethods = effectivelyPublicMethods, ) } @@ -72,7 +72,7 @@ internal class ClassNameAndAnnotationsVisitor(private val logger: Logger) : Clas ) { // This _must_ not be canonicalized, unless we also change accesses to be dotty instead of slashy this.superClassName = superName - this.interfaces = interfaces?.toSortedSet().orEmpty() + // this.interfaces = interfaces?.toSortedSet().orEmpty() className = canonicalize(name) if (interfaces?.contains("java/lang/annotation/Annotation") == true) { @@ -107,15 +107,15 @@ internal class ClassNameAndAnnotationsVisitor(private val logger: Logger) : Clas methods.add(Method(descriptor)) } - if (isEffectivelyPublic(access)) { - effectivelyPublicMethods.add( - Member.Method( - access = access, - name = name, - descriptor = descriptor, - ) - ) - } + // if (isEffectivelyPublic(access)) { + // effectivelyPublicMethods.add( + // Member.Method( + // access = access, + // name = name, + // descriptor = descriptor, + // ) + // ) + // } return null } @@ -131,15 +131,15 @@ internal class ClassNameAndAnnotationsVisitor(private val logger: Logger) : Clas constantClasses.add(name) } - if (isEffectivelyPublic(access)) { - effectivelyPublicFields.add( - Member.Field( - access = access, - name = name, - descriptor = descriptor, - ) - ) - } + // if (isEffectivelyPublic(access)) { + // effectivelyPublicFields.add( + // Member.Field( + // access = access, + // name = name, + // descriptor = descriptor, + // ) + // ) + // } return null } diff --git a/src/main/kotlin/com/autonomousapps/internal/models.kt b/src/main/kotlin/com/autonomousapps/internal/models.kt index e9a855a88..8aedd40c4 100644 --- a/src/main/kotlin/com/autonomousapps/internal/models.kt +++ b/src/main/kotlin/com/autonomousapps/internal/models.kt @@ -6,8 +6,6 @@ import com.autonomousapps.internal.asm.Opcodes import com.autonomousapps.internal.utils.efficient import com.autonomousapps.internal.utils.filterNotToSet import com.autonomousapps.internal.utils.mapToSet -import com.autonomousapps.model.internal.intermediates.producer.BinaryClass -import com.autonomousapps.model.internal.intermediates.producer.Member import com.squareup.moshi.JsonClass import java.lang.annotation.RetentionPolicy import java.util.regex.Pattern @@ -45,13 +43,13 @@ internal data class AnalyzedClass( val methods: Set, val innerClasses: Set, val constantFields: Set, - val binaryClass: BinaryClass, + // val binaryClass: BinaryClass, ) : Comparable { constructor( className: String, outerClassName: String?, superClassName: String, - interfaces: Set, + // interfaces: Set, retentionPolicy: String?, isAnnotation: Boolean, hasNoMembers: Boolean, @@ -59,8 +57,8 @@ internal data class AnalyzedClass( methods: Set, innerClasses: Set, constantClasses: Set, - effectivelyPublicFields: Set, - effectivelyPublicMethods: Set, + // effectivelyPublicFields: Set, + // effectivelyPublicMethods: Set, ) : this( className = className, outerClassName = outerClassName, @@ -71,13 +69,13 @@ internal data class AnalyzedClass( methods = methods, innerClasses = innerClasses, constantFields = constantClasses, - binaryClass = BinaryClass( - className = className.replace('.', '/'), - superClassName = superClassName.replace('.', '/'), - interfaces = interfaces, - effectivelyPublicFields = effectivelyPublicFields, - effectivelyPublicMethods = effectivelyPublicMethods, - ), + // binaryClass = BinaryClass( + // className = className.replace('.', '/'), + // superClassName = superClassName.replace('.', '/'), + // interfaces = interfaces, + // effectivelyPublicFields = effectivelyPublicFields, + // effectivelyPublicMethods = effectivelyPublicMethods, + // ), ) companion object { diff --git a/src/main/kotlin/com/autonomousapps/model/internal/Capability.kt b/src/main/kotlin/com/autonomousapps/model/internal/Capability.kt index e09fce465..38d41e65c 100644 --- a/src/main/kotlin/com/autonomousapps/model/internal/Capability.kt +++ b/src/main/kotlin/com/autonomousapps/model/internal/Capability.kt @@ -102,131 +102,131 @@ internal data class AnnotationProcessorCapability( } } -@TypeLabel("binaryClass") -@JsonClass(generateAdapter = false) -internal data class BinaryClassCapability( - val binaryClasses: Set, -) : Capability() { - - internal data class PartitionResult( - val matchingClasses: Set, - val nonMatchingClasses: Set, - ) { - - companion object { - fun empty(): PartitionResult = PartitionResult(emptySet(), emptySet()) - } - - class Builder { - val matchingClasses = sortedSetOf() - val nonMatchingClasses = sortedSetOf() - - fun build(): PartitionResult { - return PartitionResult( - matchingClasses = matchingClasses, - nonMatchingClasses = nonMatchingClasses, - ) - } - } - } - - override fun merge(other: Capability): Capability { - return BinaryClassCapability( - binaryClasses = binaryClasses + (other as BinaryClassCapability).binaryClasses, - ) - } - - internal fun findMatchingClasses(memberAccess: MemberAccess): PartitionResult { - val relevant = findRelevantBinaryClasses(memberAccess) - - // lenient - if (relevant.isEmpty()) return PartitionResult.empty() - - return relevant - .map { bin -> bin.partition(memberAccess) } - .fold(PartitionResult.Builder()) { acc, (match, nonMatch) -> - acc.apply { - match?.let { matchingClasses.add(it) } - nonMatch?.let { nonMatchingClasses.add(it) } - } - } - .build() - } - - /** - * Example: - * 1. [memberAccess] is for `groovy/lang/MetaClass#getProperty`. - * 2. That method is actually provided by `groovy/lang/MetaObjectProtocol`, which `groovy/lang/MetaClass` implements. - * - * All of the above ("this" class, its super class, and its interfaces) are relevant for search purposes. Note we - * don't inspect the member names for this check. - */ - private fun findRelevantBinaryClasses(memberAccess: MemberAccess): Set { - // direct references - val relevant = binaryClasses.filterTo(mutableSetOf()) { bin -> - bin.className == memberAccess.owner - } - - // Walk up the class hierarchy - fun walkUp(): Int { - binaryClasses.filterTo(relevant) { bin -> - bin.className in relevant.map { it.superClassName } - || bin.className in relevant.flatMap { it.interfaces } - } - return relevant.size - } - - // TODO(tsr): this could be more performant - do { - val size = relevant.size - val newSize = walkUp() - } while (newSize > size) - - return relevant - } - - /** - * Partitions and returns artificial pair of [BinaryClasses][BinaryClass]. Non-null elements indicate relevant (to - * [memberAccess] matching and non-matching members of this `BinaryClass`. Matching members are binary-compatible; and - * non-matching members have the same [name][com.autonomousapps.model.intermediates.producer.Member.name] but - * incompatible [descriptors][com.autonomousapps.model.intermediates.producer.Member.descriptor], and are therefore - * binary-incompatible. - * - * nb: We don't want this as a method directly in BinaryClass because it can't safely assert the prerequisite that - * it's only called on "relevant" classes. THIS class, however, can, via findRelevantBinaryClasses. - */ - private fun BinaryClass.partition(memberAccess: MemberAccess): Pair { - // There can be only one match - val matchingFields = effectivelyPublicFields.firstOrNull { it.matches(memberAccess) } - val matchingMethods = effectivelyPublicMethods.firstOrNull { it.matches(memberAccess) } - - // There can be many non-matches - val nonMatchingFields = effectivelyPublicFields.filterToOrderedSet { it.doesNotMatch(memberAccess) } - val nonMatchingMethods = effectivelyPublicMethods.filterToOrderedSet { it.doesNotMatch(memberAccess) } - - // Create a view of the binary class containing only the matching members. - val match = if (matchingFields != null || matchingMethods != null) { - copy( - effectivelyPublicFields = matchingFields?.let { setOf(it) }.orEmpty(), - effectivelyPublicMethods = matchingMethods?.let { setOf(it) }.orEmpty() - ) - } else { - null - } - - // Create a view of the binary class containing only the non-matching members. - val nonMatch = if (nonMatchingFields.isNotEmpty() || nonMatchingMethods.isNotEmpty()) { - copy( - effectivelyPublicFields = nonMatchingFields, - effectivelyPublicMethods = nonMatchingMethods, - ) - } else { - null - } - - return match to nonMatch - } -} +// @TypeLabel("binaryClass") +// @JsonClass(generateAdapter = false) +// internal data class BinaryClassCapability( +// val binaryClasses: Set, +// ) : Capability() { +// +// internal data class PartitionResult( +// val matchingClasses: Set, +// val nonMatchingClasses: Set, +// ) { +// +// companion object { +// fun empty(): PartitionResult = PartitionResult(emptySet(), emptySet()) +// } +// +// class Builder { +// val matchingClasses = sortedSetOf() +// val nonMatchingClasses = sortedSetOf() +// +// fun build(): PartitionResult { +// return PartitionResult( +// matchingClasses = matchingClasses, +// nonMatchingClasses = nonMatchingClasses, +// ) +// } +// } +// } +// +// override fun merge(other: Capability): Capability { +// return BinaryClassCapability( +// binaryClasses = binaryClasses + (other as BinaryClassCapability).binaryClasses, +// ) +// } +// +// internal fun findMatchingClasses(memberAccess: MemberAccess): PartitionResult { +// val relevant = findRelevantBinaryClasses(memberAccess) +// +// // lenient +// if (relevant.isEmpty()) return PartitionResult.empty() +// +// return relevant +// .map { bin -> bin.partition(memberAccess) } +// .fold(PartitionResult.Builder()) { acc, (match, nonMatch) -> +// acc.apply { +// match?.let { matchingClasses.add(it) } +// nonMatch?.let { nonMatchingClasses.add(it) } +// } +// } +// .build() +// } +// +// /** +// * Example: +// * 1. [memberAccess] is for `groovy/lang/MetaClass#getProperty`. +// * 2. That method is actually provided by `groovy/lang/MetaObjectProtocol`, which `groovy/lang/MetaClass` implements. +// * +// * All of the above ("this" class, its super class, and its interfaces) are relevant for search purposes. Note we +// * don't inspect the member names for this check. +// */ +// private fun findRelevantBinaryClasses(memberAccess: MemberAccess): Set { +// // direct references +// val relevant = binaryClasses.filterTo(mutableSetOf()) { bin -> +// bin.className == memberAccess.owner +// } +// +// // Walk up the class hierarchy +// fun walkUp(): Int { +// binaryClasses.filterTo(relevant) { bin -> +// bin.className in relevant.map { it.superClassName } +// || bin.className in relevant.flatMap { it.interfaces } +// } +// return relevant.size +// } +// +// // TODO(tsr): this could be more performant +// do { +// val size = relevant.size +// val newSize = walkUp() +// } while (newSize > size) +// +// return relevant +// } +// +// /** +// * Partitions and returns artificial pair of [BinaryClasses][BinaryClass]. Non-null elements indicate relevant (to +// * [memberAccess] matching and non-matching members of this `BinaryClass`. Matching members are binary-compatible; and +// * non-matching members have the same [name][com.autonomousapps.model.intermediates.producer.Member.name] but +// * incompatible [descriptors][com.autonomousapps.model.intermediates.producer.Member.descriptor], and are therefore +// * binary-incompatible. +// * +// * nb: We don't want this as a method directly in BinaryClass because it can't safely assert the prerequisite that +// * it's only called on "relevant" classes. THIS class, however, can, via findRelevantBinaryClasses. +// */ +// private fun BinaryClass.partition(memberAccess: MemberAccess): Pair { +// // There can be only one match +// val matchingFields = effectivelyPublicFields.firstOrNull { it.matches(memberAccess) } +// val matchingMethods = effectivelyPublicMethods.firstOrNull { it.matches(memberAccess) } +// +// // There can be many non-matches +// val nonMatchingFields = effectivelyPublicFields.filterToOrderedSet { it.doesNotMatch(memberAccess) } +// val nonMatchingMethods = effectivelyPublicMethods.filterToOrderedSet { it.doesNotMatch(memberAccess) } +// +// // Create a view of the binary class containing only the matching members. +// val match = if (matchingFields != null || matchingMethods != null) { +// copy( +// effectivelyPublicFields = matchingFields?.let { setOf(it) }.orEmpty(), +// effectivelyPublicMethods = matchingMethods?.let { setOf(it) }.orEmpty() +// ) +// } else { +// null +// } +// +// // Create a view of the binary class containing only the non-matching members. +// val nonMatch = if (nonMatchingFields.isNotEmpty() || nonMatchingMethods.isNotEmpty()) { +// copy( +// effectivelyPublicFields = nonMatchingFields, +// effectivelyPublicMethods = nonMatchingMethods, +// ) +// } else { +// null +// } +// +// return match to nonMatch +// } +// } @TypeLabel("class") @JsonClass(generateAdapter = false) diff --git a/src/main/kotlin/com/autonomousapps/model/internal/ProjectVariant.kt b/src/main/kotlin/com/autonomousapps/model/internal/ProjectVariant.kt index 2da035ce5..e3666b326 100644 --- a/src/main/kotlin/com/autonomousapps/model/internal/ProjectVariant.kt +++ b/src/main/kotlin/com/autonomousapps/model/internal/ProjectVariant.kt @@ -98,15 +98,15 @@ internal data class ProjectVariant( codeSource.flatMapToOrderedSet { it.imports } } - /** - * Every member access from this project to classes in another module. cf [usedClasses], which is a flat set of - * referenced class names. - */ - val memberAccesses: Set by unsafeLazy { - codeSource.flatMapToOrderedSet { src -> - src.binaryClassAccesses.entries.flatMap { entry -> entry.value } - } - } + // /** + // * Every member access from this project to classes in another module. cf [usedClasses], which is a flat set of + // * referenced class names. + // */ + // val memberAccesses: Set by unsafeLazy { + // codeSource.flatMapToOrderedSet { src -> + // src.binaryClassAccesses.entries.flatMap { entry -> entry.value } + // } + // } val javaImports: Set by unsafeLazy { codeSource.filter { it.kind == Kind.JAVA } diff --git a/src/main/kotlin/com/autonomousapps/model/internal/Source.kt b/src/main/kotlin/com/autonomousapps/model/internal/Source.kt index 2d9bfa98c..314dd6092 100644 --- a/src/main/kotlin/com/autonomousapps/model/internal/Source.kt +++ b/src/main/kotlin/com/autonomousapps/model/internal/Source.kt @@ -68,8 +68,8 @@ internal data class CodeSource( /** Every import in this source file. */ val imports: Set, - /** Every [MemberAccess] to another class from [this class][className]. */ - val binaryClassAccesses: Map>, + // /** Every [MemberAccess] to another class from [this class][className]. */ + // val binaryClassAccesses: Map>, ) : Source(relativePath) { enum class Kind { diff --git a/src/main/kotlin/com/autonomousapps/model/internal/intermediates/ExplodingJar.kt b/src/main/kotlin/com/autonomousapps/model/internal/intermediates/ExplodingJar.kt index 70e3d90c7..fd73bb65e 100644 --- a/src/main/kotlin/com/autonomousapps/model/internal/intermediates/ExplodingJar.kt +++ b/src/main/kotlin/com/autonomousapps/model/internal/intermediates/ExplodingJar.kt @@ -8,7 +8,6 @@ import com.autonomousapps.internal.ClassNames import com.autonomousapps.internal.utils.mapToOrderedSet import com.autonomousapps.internal.utils.reallyAll import com.autonomousapps.model.internal.KtFile -import com.autonomousapps.model.internal.intermediates.producer.BinaryClass import java.lang.annotation.RetentionPolicy /** @@ -39,11 +38,11 @@ internal class ExplodingJar( val androidLintRegistry: String? ) { - /** - * The set of classes provided by this jar, including information about their superclass, interfaces, and public - * members. May be empty. cf [classNames]. - */ - val binaryClasses: Set = analyzedClasses.mapToOrderedSet { it.binaryClass } + // /** + // * The set of classes provided by this jar, including information about their superclass, interfaces, and public + // * members. May be empty. cf [classNames]. + // */ + // val binaryClasses: Set = analyzedClasses.mapToOrderedSet { it.binaryClass } /** The set of classes provided by this jar. May be empty. cf [binaryClasses]. */ val classNames: Set = analyzedClasses.mapToOrderedSet { it.className } diff --git a/src/main/kotlin/com/autonomousapps/model/internal/intermediates/producer/ExplodedJar.kt b/src/main/kotlin/com/autonomousapps/model/internal/intermediates/producer/ExplodedJar.kt index a0ca51f19..3489d14d0 100644 --- a/src/main/kotlin/com/autonomousapps/model/internal/intermediates/producer/ExplodedJar.kt +++ b/src/main/kotlin/com/autonomousapps/model/internal/intermediates/producer/ExplodedJar.kt @@ -3,16 +3,9 @@ package com.autonomousapps.model.internal.intermediates.producer import com.autonomousapps.internal.utils.ifNotEmpty -import com.autonomousapps.model.internal.AndroidLinterCapability -import com.autonomousapps.model.internal.BinaryClassCapability -import com.autonomousapps.model.internal.Capability -import com.autonomousapps.model.internal.ClassCapability -import com.autonomousapps.model.internal.ConstantCapability import com.autonomousapps.model.Coordinates -import com.autonomousapps.model.internal.InferredCapability -import com.autonomousapps.model.internal.KtFile +import com.autonomousapps.model.internal.* import com.autonomousapps.model.internal.PhysicalArtifact -import com.autonomousapps.model.internal.SecurityProviderCapability import com.autonomousapps.model.internal.intermediates.DependencyView import com.autonomousapps.model.internal.intermediates.ExplodingJar import com.squareup.moshi.JsonClass @@ -47,10 +40,10 @@ internal data class ExplodedJar( * [androidLintRegistry] must be non-null. */ val isLintJar: Boolean = false, - /** - * The classes (with binary member signatures) provided by this library. - */ - val binaryClasses: Set, + // /** + // * The classes (with binary member signatures) provided by this library. + // */ + // val binaryClasses: Set, /** * The classes declared by this library. */ @@ -76,7 +69,7 @@ internal data class ExplodedJar( securityProviders = exploding.securityProviders, androidLintRegistry = exploding.androidLintRegistry, isLintJar = exploding.isLintJar, - binaryClasses = exploding.binaryClasses, + // binaryClasses = exploding.binaryClasses, classes = exploding.classNames, constantFields = exploding.constants, ktFiles = exploding.ktFiles @@ -97,7 +90,7 @@ internal data class ExplodedJar( override fun toCapabilities(): List { val capabilities = mutableListOf() capabilities += InferredCapability(isCompileOnlyAnnotations) - binaryClasses.ifNotEmpty { capabilities += BinaryClassCapability(it) } + // binaryClasses.ifNotEmpty { capabilities += BinaryClassCapability(it) } classes.ifNotEmpty { capabilities += ClassCapability(it) } constantFields.ifNotEmpty { capabilities += ConstantCapability(it, ktFiles) } securityProviders.ifNotEmpty { capabilities += SecurityProviderCapability(it) } diff --git a/src/main/kotlin/com/autonomousapps/tasks/ComputeUsagesTask.kt b/src/main/kotlin/com/autonomousapps/tasks/ComputeUsagesTask.kt index c5cb22d83..54c3b5137 100644 --- a/src/main/kotlin/com/autonomousapps/tasks/ComputeUsagesTask.kt +++ b/src/main/kotlin/com/autonomousapps/tasks/ComputeUsagesTask.kt @@ -3,32 +3,14 @@ package com.autonomousapps.tasks import com.autonomousapps.internal.utils.* -import com.autonomousapps.model.* +import com.autonomousapps.model.Coordinates +import com.autonomousapps.model.DuplicateClass import com.autonomousapps.model.declaration.internal.Bucket import com.autonomousapps.model.declaration.internal.Declaration -import com.autonomousapps.model.internal.AndroidAssetCapability -import com.autonomousapps.model.internal.AndroidLinterCapability -import com.autonomousapps.model.internal.AndroidManifestCapability -import com.autonomousapps.model.internal.AndroidResCapability -import com.autonomousapps.model.internal.AndroidResSource -import com.autonomousapps.model.internal.AnnotationProcessorCapability -import com.autonomousapps.model.internal.BinaryClassCapability -import com.autonomousapps.model.internal.ClassCapability -import com.autonomousapps.model.internal.ConstantCapability -import com.autonomousapps.model.internal.Dependency -import com.autonomousapps.model.internal.DependencyGraphView -import com.autonomousapps.model.internal.InferredCapability -import com.autonomousapps.model.internal.InlineMemberCapability -import com.autonomousapps.model.internal.NativeLibCapability -import com.autonomousapps.model.internal.ProjectVariant -import com.autonomousapps.model.internal.SecurityProviderCapability -import com.autonomousapps.model.internal.ServiceLoaderCapability -import com.autonomousapps.model.internal.TypealiasCapability +import com.autonomousapps.model.internal.* import com.autonomousapps.model.internal.intermediates.DependencyTraceReport import com.autonomousapps.model.internal.intermediates.DependencyTraceReport.Kind import com.autonomousapps.model.internal.intermediates.Reason -import com.autonomousapps.model.internal.intermediates.consumer.MemberAccess -import com.autonomousapps.model.internal.intermediates.producer.BinaryClass import com.autonomousapps.visitor.GraphViewReader import com.autonomousapps.visitor.GraphViewVisitor import org.gradle.api.DefaultTask @@ -187,9 +169,9 @@ private class GraphVisitor( isAnnotationProcessorCandidate = usesAnnotationProcessor(dependencyCoordinates, capability, context) } - is BinaryClassCapability -> { - checkBinaryCompatibility(dependencyCoordinates, capability, context) - } + // is BinaryClassCapability -> { + // checkBinaryCompatibility(dependencyCoordinates, capability, context) + // } is ClassCapability -> { // We want to track this in addition to tracking one of the below, so it's not part of the same if/else-if @@ -414,100 +396,100 @@ private class GraphVisitor( } } - private fun checkBinaryCompatibility( - coordinates: Coordinates, - binaryClassCapability: BinaryClassCapability, - context: GraphViewVisitor.Context, - ) { - // Can't be incompatible if the code compiles in the context of no duplication - if (context.duplicateClasses.isEmpty()) return - - // TODO(tsr): special handling for @Composable - val memberAccessOwners = context.project.memberAccesses.mapToSet { it.owner } - val relevantDuplicates = context.duplicateClasses - .filter { duplicate -> coordinates in duplicate.dependencies && duplicate.className in memberAccessOwners } - .filter { duplicate -> duplicate.classpathName == DuplicateClass.COMPILE_CLASSPATH_NAME } - - // Can't be incompatible if the code compiles in the context of no relevant duplication - if (relevantDuplicates.isEmpty()) return - - val relevantDuplicateClassNames = relevantDuplicates.mapToOrderedSet { it.className } - val relevantMemberAccesses = context.project.memberAccesses - .filterToOrderedSet { access -> access.owner in relevantDuplicateClassNames } - - val partitionResult = relevantMemberAccesses.mapToSet { access -> - binaryClassCapability.findMatchingClasses(access) - }.reduce() - val matchingBinaryClasses = partitionResult.matchingClasses - val nonMatchingBinaryClasses = partitionResult.nonMatchingClasses - - // There must be a compatible BinaryClass. for each MemberAccess for the usage to be binary-compatible - val isBinaryCompatible = relevantMemberAccesses.all { access -> - when (access) { - is MemberAccess.Field -> { - matchingBinaryClasses.any { bin -> - bin.effectivelyPublicFields.any { field -> - field.matches(access) - } - } - } - - is MemberAccess.Method -> { - matchingBinaryClasses.any { bin -> - bin.effectivelyPublicMethods.any { method -> - method.matches(access) - } - } - } - } - } - - if (!isBinaryCompatible) { - reportBuilder[coordinates, Kind.DEPENDENCY] = Reason.BinaryIncompatible( - relevantMemberAccesses, nonMatchingBinaryClasses - ) - } - } - - // TODO: I think this could be more efficient - private fun Set.reduce(): BinaryClassCapability.PartitionResult { - val matches = sortedSetOf() - val nonMatches = sortedSetOf() - - forEach { result -> - matches.addAll(result.matchingClasses) - nonMatches.addAll(result.nonMatchingClasses) - } - - return BinaryClassCapability.PartitionResult( - matchingClasses = matches.reduce(), - nonMatchingClasses = nonMatches.reduce(), - ) - } - - private fun Set.reduce(): Set { - val builders = mutableMapOf() - - forEach { bin -> - builders.merge( - bin.className, - BinaryClass.Builder( - className = bin.className, - superClassName = bin.superClassName, - interfaces = bin.interfaces.toSortedSet(), - effectivelyPublicFields = bin.effectivelyPublicFields.toSortedSet(), - effectivelyPublicMethods = bin.effectivelyPublicMethods.toSortedSet(), - ) - ) { acc, inc -> - acc.apply { - effectivelyPublicFields.addAll(inc.effectivelyPublicFields) - effectivelyPublicMethods.addAll(inc.effectivelyPublicMethods) - } - } - } - - return builders.values.mapToOrderedSet { it.build() } - } + // private fun checkBinaryCompatibility( + // coordinates: Coordinates, + // binaryClassCapability: BinaryClassCapability, + // context: GraphViewVisitor.Context, + // ) { + // // Can't be incompatible if the code compiles in the context of no duplication + // if (context.duplicateClasses.isEmpty()) return + // + // // TODO(tsr): special handling for @Composable + // val memberAccessOwners = context.project.memberAccesses.mapToSet { it.owner } + // val relevantDuplicates = context.duplicateClasses + // .filter { duplicate -> coordinates in duplicate.dependencies && duplicate.className in memberAccessOwners } + // .filter { duplicate -> duplicate.classpathName == DuplicateClass.COMPILE_CLASSPATH_NAME } + // + // // Can't be incompatible if the code compiles in the context of no relevant duplication + // if (relevantDuplicates.isEmpty()) return + // + // val relevantDuplicateClassNames = relevantDuplicates.mapToOrderedSet { it.className } + // val relevantMemberAccesses = context.project.memberAccesses + // .filterToOrderedSet { access -> access.owner in relevantDuplicateClassNames } + // + // val partitionResult = relevantMemberAccesses.mapToSet { access -> + // binaryClassCapability.findMatchingClasses(access) + // }.reduce() + // val matchingBinaryClasses = partitionResult.matchingClasses + // val nonMatchingBinaryClasses = partitionResult.nonMatchingClasses + // + // // There must be a compatible BinaryClass. for each MemberAccess for the usage to be binary-compatible + // val isBinaryCompatible = relevantMemberAccesses.all { access -> + // when (access) { + // is MemberAccess.Field -> { + // matchingBinaryClasses.any { bin -> + // bin.effectivelyPublicFields.any { field -> + // field.matches(access) + // } + // } + // } + // + // is MemberAccess.Method -> { + // matchingBinaryClasses.any { bin -> + // bin.effectivelyPublicMethods.any { method -> + // method.matches(access) + // } + // } + // } + // } + // } + // + // if (!isBinaryCompatible) { + // reportBuilder[coordinates, Kind.DEPENDENCY] = Reason.BinaryIncompatible( + // relevantMemberAccesses, nonMatchingBinaryClasses + // ) + // } + // } + // + // // TODO: I think this could be more efficient + // private fun Set.reduce(): BinaryClassCapability.PartitionResult { + // val matches = sortedSetOf() + // val nonMatches = sortedSetOf() + // + // forEach { result -> + // matches.addAll(result.matchingClasses) + // nonMatches.addAll(result.nonMatchingClasses) + // } + // + // return BinaryClassCapability.PartitionResult( + // matchingClasses = matches.reduce(), + // nonMatchingClasses = nonMatches.reduce(), + // ) + // } + // + // private fun Set.reduce(): Set { + // val builders = mutableMapOf() + // + // forEach { bin -> + // builders.merge( + // bin.className, + // BinaryClass.Builder( + // className = bin.className, + // superClassName = bin.superClassName, + // interfaces = bin.interfaces.toSortedSet(), + // effectivelyPublicFields = bin.effectivelyPublicFields.toSortedSet(), + // effectivelyPublicMethods = bin.effectivelyPublicMethods.toSortedSet(), + // ) + // ) { acc, inc -> + // acc.apply { + // effectivelyPublicFields.addAll(inc.effectivelyPublicFields) + // effectivelyPublicMethods.addAll(inc.effectivelyPublicMethods) + // } + // } + // } + // + // return builders.values.mapToOrderedSet { it.build() } + // } private fun isImplementation( coordinates: Coordinates, diff --git a/src/main/kotlin/com/autonomousapps/tasks/SynthesizeProjectViewTask.kt b/src/main/kotlin/com/autonomousapps/tasks/SynthesizeProjectViewTask.kt index 2d240ad0c..4c7fbc7d4 100644 --- a/src/main/kotlin/com/autonomousapps/tasks/SynthesizeProjectViewTask.kt +++ b/src/main/kotlin/com/autonomousapps/tasks/SynthesizeProjectViewTask.kt @@ -62,7 +62,7 @@ abstract class SynthesizeProjectViewTask @Inject constructor( @get:InputFile abstract val graph: RegularFileProperty - /** [`Set`][com.autonomousapps.model.intermediates.AnnotationProcessorDependency] */ + /** [`Set`][com.autonomousapps.model.internal.intermediates.AnnotationProcessorDependency] */ @get:PathSensitive(PathSensitivity.NONE) @get:InputFile abstract val annotationProcessors: RegularFileProperty @@ -180,14 +180,13 @@ abstract class SynthesizeProjectViewTask @Inject constructor( nonAnnotationClasses.addAll(bytecode.nonAnnotationClasses) annotationClasses.addAll(bytecode.annotationClasses) invisibleAnnotationClasses.addAll(bytecode.invisibleAnnotationClasses) - - // TODO(tsr): flatten into a single set? Do we need the map? - // Merge the two maps - bytecode.binaryClassAccesses.forEach { (className, memberAccesses) -> - binaryClassAccesses.merge(className, memberAccesses.toMutableSet()) { acc, inc -> - acc.apply { addAll(inc) } - } - } + // // TODO(tsr): flatten into a single set? Do we need the map? + // // Merge the two maps + // bytecode.binaryClassAccesses.forEach { (className, memberAccesses) -> + // binaryClassAccesses.merge(className, memberAccesses.toMutableSet()) { acc, inc -> + // acc.apply { addAll(inc) } + // } + // } }, CodeSourceBuilder::concat ) @@ -275,7 +274,7 @@ private class CodeSourceBuilder(val className: String) { val invisibleAnnotationClasses = mutableSetOf() val exposedClasses = mutableSetOf() val imports = mutableSetOf() - val binaryClassAccesses = mutableMapOf>() + // val binaryClassAccesses = mutableMapOf>() fun concat(other: CodeSourceBuilder): CodeSourceBuilder { nonAnnotationClasses.addAll(other.nonAnnotationClasses) @@ -299,7 +298,7 @@ private class CodeSourceBuilder(val className: String) { usedInvisibleAnnotationClasses = invisibleAnnotationClasses, exposedClasses = exposedClasses, imports = imports, - binaryClassAccesses = binaryClassAccesses, + // binaryClassAccesses = binaryClassAccesses, ) } }