18

I'm having issues with Bouncycastle, which only arise when running the :lint task.

Generally it seems to be a Java 9 byte-code version 53.0 / ASM version conflict.

These are the dependencies:

// https://mvnrepository.com/artifact/org.bouncycastle
implementation "org.bouncycastle:bcprov-jdk15on:1.64"
implementation "org.bouncycastle:bcpkix-jdk15on:1.64"

Which cause the :lint task to throw processing errors:

> Task :mobile:lint
Error processing bcpkix-jdk15on-1.64.jar:META-INF/versions/9/module-info.class: broken class file? (This feature requires ASM6)
Error processing bcprov-jdk15on-1.64.jar:META-INF/versions/9/module-info.class: broken class file? (This feature requires ASM6)

META-INF/versions/9/module-info.class: broken class file? (This feature requires ASM6)

The same goes for:

// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation "com.google.code.gson:gson:2.8.6"

Since upgrading from 1.4.1 to 1.4.2-native-mt, it's the same again:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2-native-mt"

kotlin-stdlib-1.4.0.jar:META-INF\versions\9\module-info.class: broken class file? (Module requires ASM6)

Martin Zeitler
  • 1
  • 19
  • 155
  • 216

5 Answers5

6

As already mentioned this was introduced in Java 9, that Android does not support. You could just use packagingOptions to remove those classes.

android {
    packagingOptions {
        exclude "**/module-info.class"
    }
}

This should not affect actual executed code and should also remove classes for lint checks as lint is working on bytecode.

Blaz
  • 1,935
  • 15
  • 14
  • 2
    Have already tried that before asking (in all possible variations), it's not working... because the packaging obviously happens after the processing. – Martin Zeitler Mar 28 '20 at 11:20
4

Update: Please see my current answer, which nails the problem.
This answer is only being kept as an example for Gradle scripting.


When using old versions (likely built with Java 8), there are no such processing errors:

// https://mvnrepository.com/artifact/org.bouncycastle
implementation "org.bouncycastle:bcprov-jdk15on:1.60"
implementation "org.bouncycastle:bcpkix-jdk15on:1.60"

// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation "com.google.code.gson:gson:2.8.5"

The issue obviously was introduced with version 1.61 / 2.8.6 (likely built with Java 9).


It's annoying when Google brings one back to the own answer, which is not really an answer. Instead of keeping back the version or editing the JAR, I've wrote a DeleteModuleInfoTask and a shell script, which automates the deletion of module-info.class from any given Java dependency.

Since commandLine only accepts a single command, one almost has to call a script. And this should serve as a good example for a custom Exec task.

For Linux: module_info.sh considers versions/9/module-info.class and module-info.class:

#!/usr/bin/env bash
GRADLE_CACHE_DIR=$HOME/.gradle/caches/modules-2/files-2.1
ZIP_PATHS=(META-INF/versions/9/module-info.class module-info.class)   
if [[ $# -ne 3 ]]; then
  echo "Illegal number of parameters"
  exit 1
else
  if [ -d "$GRADLE_CACHE_DIR" ]; then
    DIRNAME=${GRADLE_CACHE_DIR}/$1/$2/$3
    if [ -d "$DIRNAME" ]; then
      cd ${DIRNAME} || exit 1
      find . -name ${2}-${3}.jar | (
        read ITEM;
        for ZIP_PATH in "${ZIP_PATHS[@]}"; do
          INFO=$(zipinfo ${ITEM} ${ZIP_PATH} 2>&1)
          if [ "${INFO}" != "caution: filename not matched:  ${ZIP_PATH}" ]; then
            zip ${ITEM} -d ${ZIP_PATH} # > /dev/null 2>&1
          fi
        done
      )
      exit 0
    fi
  fi
fi

For Windows: module_info.bat depends on 7-Zip:

@echo off
REM delete module.info from JAR file - may interfere with the local IDE.
for /R %USERPROFILE%\.gradle\caches\modules-2\files-2.1\%1\%2\%3\ %%G in (%2-%3.jar) do (
  if exist %%G (
      7z d  %%G META-INF\versions\9\module-info.class > NUL:
      7z d  %%G versions\9\module-info.class > NUL:
      7z d  %%G module-info.class > NUL:
  )
) 

Update: After some testing I came to the conclusion that it may be better to manually edit the file when developing on Windows, because Android Studio and Java will lock the JAR, which will subsequently prevent the edit and leave the temp file behind.


File tasks.gradle provides the DeleteModuleInfoTask:

import javax.inject.Inject

abstract class DeleteModuleInfoTask extends Exec {
    @Inject
    DeleteModuleInfoTask(String dependency) {
        def os = org.gradle.internal.os.OperatingSystem.current()
        def stdout = new ByteArrayOutputStream()
        def stderr = new ByteArrayOutputStream()
        ignoreExitValue true
        standardOutput stdout
        errorOutput stderr
        workingDir "${getProject().getGradle().getGradleUserHomeDir()}${File.separator}caches${File.separator}modules-2${File.separator}files-2.1${File.separator}${dependency.replace(":", File.separator).toString()}"
        String script = "${getProject().getRootDir().getAbsolutePath()}${File.separator}scripts${File.separator}"
        def prefix = ""; def suffix = "sh"
        if (os.isWindows()) {prefix = "cmd /c "; suffix = "bat"}
        String[] item = dependency.split(":")
        commandLine "${prefix}${script}module_info.${suffix} ${item[0]} ${item[1]} ${item[2]}".split(" ")
        // doFirst {println "${commandLine}"}
        doLast {
            if (execResult.getExitValue() == 0) {
                if (stdout.toString() != "") {
                    println "> Task :${project.name}:${name} ${stdout.toString()}"
                }
            } else {
                println "> Task :${project.name}:${name} ${stderr.toString()}"
            }
        }
    }
}

Example Usage:

// Bouncycastle
tasks.register("lintFixModuleInfoBcPkix", DeleteModuleInfoTask, "org.bouncycastle:bcpkix-jdk15on:1.64")
lint.dependsOn lintFixModuleInfoBcPkix

tasks.register("lintFixModuleInfoBcProv", DeleteModuleInfoTask, "org.bouncycastle:bcprov-jdk15on:1.64")
lint.dependsOn lintFixModuleInfoBcProv

// GSON
tasks.register("lintFixModuleInfoGson", DeleteModuleInfoTask, "com.google.code.gson:gson:2.8.6")
lint.dependsOn lintFixModuleInfoGson

// Kotlin Standard Library
tasks.register("lintFixModuleInfoKotlinStdLib", DeleteModuleInfoTask, "org.jetbrains.kotlin:kotlin-stdlib:1.4.32")
lint.dependsOn lintFixModuleInfoKotlinStdLib

Make sure to register these tasks only for a single module.

According to resmon "Resource Monitor" > "Associated Handles", studio64 and java may hold a lock on the JAR file, therefore 7-Zip may only be able to edit the archive when Android Studio and Java had been closed; at least it nicely works for CI on Linux.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • How would I execute this (I am on Windows)? Do I create the various files in for instance notepad++? If yes, where do I save them? Do I run them from Terminal in Android Studio? Sorry for my ignorance. – moster67 Mar 07 '21 at 01:29
  • Android Studio was used as editor. The Gradle task is platform-independent - for Windows you'd need `module_info.bat` and 7-Zip. When building remotely, `module_info.sh` still may be required. – Martin Zeitler Mar 07 '21 at 08:07
1

The file module-info.class is part of the Java module system which was introduced since Java 9. As per this issue on Android IssueTracker, the bug has been be fixed since Android Studio 3.4.

Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417
1

I have got the following error message:

Error processing C:\Users\mypc\.gradle\caches\modules-2\files-2.1\com.google.code.gson\gson\2.8.6\9180733b7df8542621dc12e21e87557e8c99b8cb\gson-2.8.6.jar:module-info.class: broken class file? (This feature requires ASM6)

This error occurs without using a development system like Android Studio. I use Gradle 6.1.1.

I prevented the mistake as follows:

  1. Open the file gson-2.8.6.jar which is named in the error message
  2. Removing of the file module-info.class, which is located in the root
gotwo
  • 663
  • 8
  • 16
0

There is a more simple workaround. Basically the problem can be identified as "running Gradle with Java 8, while handling files which were built with Java 9". My new approach is building with Java 11 (GitHub Actions also builds with Java 11 and Gradle 6.7.1 would currently support up to Java 15).

  • After installing Java 11 with sudo dnf install java-11-openjdk

  • alternatives --display java will list the JDK to use.

For example: /usr/lib/jvm/java-11-openjdk-11.0.11.0.9-0.el8_3.x86_64:

Android Studio Screenshot


On a side note, building with JDK 11 also fixes this warning:

Current JDK version 1.8.0_172-b11 has a bug (https://bugs.openjdk.java.net/browse/JDK-8007720) that prevents Room from being incremental. Consider using JDK 11+ or the embedded JDK shipped with Android Studio 3.5+.

The "embedded JDK shipped with Android Studio 3.5+" is still Java 8 ...

Martin Zeitler
  • 1
  • 19
  • 155
  • 216