Skip to content

Feature request: given a set of collisions, report if any are problematic #4

@autonomousapps

Description

@autonomousapps

I've wanted something like this plugin for a while, so thank you for creating it! My use-case is detecting collisions on the buildscript classpath, which has been a source of frustration in the past. So, I modified the task registered by this plugin to use that Configuration and detect collisions. I found hundreds!

// root build.gradle
tasks.named('detectCollisions') {
  configurations.setFrom(buildscript.configurations.getByName('classpath'))
  collisionFilter.exclude(
      '**/*.TXT',
      '**/*.txt',
      '**/*.properties',
      'LICENSE',
      'NOTICE',
      '**/*.kotlin_builtins',
      '**/*.proto',
      '**/*.gif',
      '**/*.xml',
      '**/*.xsd',
      '**/*.dtd',
      '**/*.rng',
  )
}

I manually inspected one of the collisions with javap and it turned out that all the duplicate class files were identical (according to a string comparison using that tool). So then I scripted that to see if there were any issues at all amongst all the collisions:

// root build.gradle
tasks.register('printCollisions', CollisionsTask) {
  // this file was generated with:
  // ./gradlew :detectCollisions &> collisions.txt
  collisions.set(file('collisions.txt'))
}

// Example collision:
//Collision detected! Entry com/sun/xml/bind/v2/model/core/NonElementRef.class present in following JARs:
//   /Users/trobalik/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-core/2.3.0/d044c784e41d026778693fb44a8026c1fd9a7506/jaxb-core-2.3.0.jar
//   /Users/trobalik/.gradle/caches/modules-2/files-2.1/com.sun.xml.bind/jaxb-impl/2.3.2/9d70d9b54cbc91b0e647d97af87395f39ea189f9/jaxb-impl-2.3.2.jar
//   /Users/trobalik/.gradle/caches/modules-2/files-2.1/org.glassfish.jaxb/jaxb-runtime/2.3.2/5528bc882ea499a09d720b42af11785c4fc6be2a/jaxb-runtime-2.3.2.jar
abstract class CollisionsTask extends DefaultTask {

  @Inject
  abstract ExecOperations getExecOps()

  @PathSensitive(PathSensitivity.RELATIVE)
  @InputFile
  abstract RegularFileProperty getCollisions()

  //javap -classpath <jar file> <class ref>
  @TaskAction
  def action() {
    def classRegex = ~/^Collision detected! Entry (.+?)\.class present in following JARs:$/

    List<Collision> chunks = []
    def lines = getCollisions().get().asFile.readLines()

    def processingCollision = false
    Collision chunk
    lines.each { line ->
      if (line.startsWith('Collision detected! Entry ')) {
        def m = classRegex.matcher(line)
        if (m.matches()) {
          def cl = m.group(1)
          chunk = new Collision(classRef: cl)
          chunks += chunk

          processingCollision = true
        }
      } else if (processingCollision) {
        if (line.trim() == '') {
          processingCollision = false
        } else {
          chunk.jars += line.trim()
        }
      }
    }

    chunks.find().each { collision ->
      Set<String> decompiled = []
      collision.jars.each { jar ->
        def out = new ByteArrayOutputStream()
        getExecOps().exec {
          it.commandLine('javap', '-classpath', jar, collision.classRef)
          it.standardOutput(out)
        }
        decompiled += out.toString()
      }

      if (decompiled.size() != 1) {
        logger.error("Class file ${collision.classRef} has incompatible definitions")
      }
    }
  }

  @Canonical
  class Collision {
    String classRef
    Set<String> jars = []
  }
}

So what I'm wondering is, are you open to this being a feature of the plugin?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions