vigna / fastutil

fastutil extends the Java™ Collections Framework by providing type-specific maps, sets, lists and queues.
Apache License 2.0
1.76k stars 196 forks source link

Packaging / Linkage error introduced in 8.5.4 #260

Closed sfc-gh-pbennes closed 3 years ago

sfc-gh-pbennes commented 3 years ago

Hello there!

I just came over from https://github.com/line/armeria/pull/3713 where I'm chasing down what looks like a packaging issue introduced during the reorganization in 8.5.4.

The problem is that there are some classes in fastutil.jar that are referenced by classes in the other jars, but the dependency between jars isn't declared so fastutil.jar isn't getting implicitly pulled in, resulting in class not found linkage error.

Armeria is depending upon and relocating fastutil-core and fastutil-extra and not fastutil, and is seeing this linkage error when building:

[ERROR] Linkage Checker rule found 6 reachable errors:
Class com.linecorp.armeria.internal.shaded.fastutil.booleans.BooleanConsumer is not found;
  referenced by 2 class files
    com.linecorp.armeria.internal.shaded.fastutil.booleans.BooleanIterable (com.linecorp.armeria:armeria:1.9.2)
    com.linecorp.armeria.internal.shaded.fastutil.booleans.BooleanIterator (com.linecorp.armeria:armeria:1.9.2)
  Cause:
    Unknown
Class com.linecorp.armeria.internal.shaded.fastutil.booleans.BooleanSpliterators is not found;
  referenced by 1 class file
    com.linecorp.armeria.internal.shaded.fastutil.booleans.BooleanIterable (com.linecorp.armeria:armeria:1.9.2)
  Cause:
    Unknown
Class com.linecorp.armeria.internal.shaded.fastutil.floats.FloatConsumer is not found;
  referenced by 3 class files
    com.linecorp.armeria.internal.shaded.fastutil.floats.FloatIterable (com.linecorp.armeria:armeria:1.9.2)
    com.linecorp.armeria.internal.shaded.fastutil.floats.FloatIterator (com.linecorp.armeria:armeria:1.9.2)
    com.linecorp.armeria.internal.shaded.fastutil.floats.FloatSpliterator (com.linecorp.armeria:armeria:1.9.2)
  Cause:
    Unknown
Class com.linecorp.armeria.internal.shaded.fastutil.floats.FloatSpliterators is not found;
  referenced by 1 class file
    com.linecorp.armeria.internal.shaded.fastutil.floats.FloatIterable (com.linecorp.armeria:armeria:1.9.2)
  Cause:
    Unknown
Class com.linecorp.armeria.internal.shaded.fastutil.shorts.ShortConsumer is not found;
  referenced by 3 class files
    com.linecorp.armeria.internal.shaded.fastutil.shorts.ShortIterable (com.linecorp.armeria:armeria:1.9.2)
    com.linecorp.armeria.internal.shaded.fastutil.shorts.ShortIterator (com.linecorp.armeria:armeria:1.9.2)
    com.linecorp.armeria.internal.shaded.fastutil.shorts.ShortSpliterator (com.linecorp.armeria:armeria:1.9.2)
  Cause:
    Unknown
Class com.linecorp.armeria.internal.shaded.fastutil.shorts.ShortSpliterators is not found;
  referenced by 1 class file
    com.linecorp.armeria.internal.shaded.fastutil.shorts.ShortIterable (com.linecorp.armeria:armeria:1.9.2)
  Cause:
    Unknown

Which makes sense as the dependency structure looks like this:

└─ it.unimi.dsi:fastutil:8.5.4
   └─ it.unimi.dsi:fastutil-extra:8.5.4
      └─ it.unimi.dsi:fastutil-core:8.5.4

This issue was caught by a build tool put out by Google: https://github.com/GoogleCloudPlatform/cloud-opensource-java/wiki/Linkage-Checker-Enforcer-Rule.

I suspect the issue is in split.sh in how it's dividing up the three jars but it's not obvious to me what the intent is. But after running split.sh it does show the placement:

$ grep /BooleanConsumer.class *.txt
fastutil-rest.txt:it/unimi/dsi/fastutil/booleans/BooleanConsumer.class
$ grep /BooleanIterator.class *.txt
fastutil-core.txt:it/unimi/dsi/fastutil/booleans/BooleanIterator.class
vigna commented 3 years ago

I don't understand—these are not my jars or classes. I mean, the code is, but it has been copied and placed somewhere else—I have no control on that and nobody reported this kind of problems.

In any case fastutil will move back to the monolithic jar + a core jar for people using only ints/longs/doubles, so that will solve the issue.

sfc-gh-pbennes commented 3 years ago

Ignore the armeria stuff. Lets look at just one issue here to make it simple to understand, BooleanIterator and BooleanConsumer.

This is the interface definition for BooleanIterator:

public interface BooleanIterator extends PrimitiveIterator<Boolean, BooleanConsumer> {

BooleanIterator is referencing BooleanConsumer so BooleanIterator has a dependency on BooleanConsumer, but those two classes are packaged in separate jars that don't have a dependency on each other:

$ unzip -l ./fastutil-core/8.5.4/fastutil-core-8.5.4.jar | grep BooleanIterator.class
     2834  03-28-2021 13:23   it/unimi/dsi/fastutil/booleans/BooleanIterator.class
$ unzip -l ./fastutil/8.5.4/fastutil-8.5.4.jar | grep BooleanConsumer.class
     2310  03-28-2021 13:25   it/unimi/dsi/fastutil/booleans/BooleanConsumer.class

BooleanIterator is in fastutil-core and BooleanConsumer is in fastutil. So fastutil-core depends on fastutil, but doesn't actually. That actually creates a circular dependency here. Does this make sense?

vigna commented 3 years ago

fastutil depends on fastutil-extra which depends on fastutil-core. There must be something wrong in your dependency management...

https://search.maven.org/artifact/it.unimi.dsi/fastutil/8.5.4/jar

sfc-gh-pbennes commented 3 years ago

Here, I've made a reproduction case in a repo at https://github.com/sfc-gh-pbennes/fastutil-example. mvn compile fails with class not found:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project bar: Compilation failure
[ERROR] /home/pbennes/vcs/fastutil-example/src/main/java/foo/Bar.java:[5,8] cannot access it.unimi.dsi.fastutil.booleans.BooleanConsumer
[ERROR]   class file for it.unimi.dsi.fastutil.booleans.BooleanConsumer not found
vigna commented 3 years ago

OK, you're right. Investigating now. This has to be fixed even if I change the distribution to fastutil-core.jar and fastutil.jar, as fastutil-core.jar would miss dependencies anyways.

vigna commented 3 years ago

It appears that my ideas about jdeps (in particular, recursive dependency computation) were wrong. I modified the splitting procedure to iterate jdeps until stability. Now BooleanConsumer appears in fastutil-core. Thanks for the report.

sfc-gh-pbennes commented 3 years ago

Thanks @vigna =)

vigna commented 3 years ago

Commit 82e2ad359fd09 should provide a working fastutil-core.jar. I would be grateful if you could test it in your case before I distributed 8.5.5.

sfc-gh-pbennes commented 3 years ago

I'll try to find some time to test 82e2ad3 and report back.

sfc-gh-pbennes commented 3 years ago

Ok, as far as I can tell, both fastutil and fastutil-core seem to be free of linkage errors.