jib has some tricks to discover the sole main class in a given classpath. It generally works well, but it fails with Scala 2.13 and JDK25. Scala 3 and JDK25 works as expected.
I filed an issue with the jib repo.
The code in question:
// For Java 25+, check flexible main method signatures (JEP 512)
boolean isValidDescriptor =
descriptor.equals(MAIN_WITH_ARGS_DESCRIPTOR)
|| descriptor.equals(MAIN_NO_ARGS_DESCRIPTOR);
if (!isValidDescriptor) {
return null;
}
int relevantAccess =
access & ~(Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED | OPTIONAL_MODIFIERS);
if (relevantAccess == Opcodes.ACC_STATIC || relevantAccess == 0) {
visitedMainClass = true;
}
Edit:
The problem is that with Scala 2 and JDK25, jib MainClassFinder sees two main classes:
example.Main
example.Main$
With every other combination only example.Main is seen.
Scala 2.13
Scala 3
Java 17
Not Tested
Java 21
Java 25
1 Like
How are you creating main?
Generally, it will generate a static forwarder from class C to C$.
I see that in its partial support of deprecated App, Scala 3 produces a synthetic bridge in the App object. Maybe that is not counted?
public void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x1041) ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
object Main extends App {
// ...
}
and
object Main {
def main(args: Array[String]): Unit = {
// ...
}
}
Both of these caused the issue.
I would expect explicit main to be identical under Scala 3 as under Scala 2 and show the same behavior, but I did not try it with Jib.
Looks like jib MainClassFinder also breaks with explicit main with Scala 3 JDK 25 as well. Curiously extends App does not break MainClassFinder with Scala 3 JDK 25.
That is likely per my comment about the additional flags in Scala 3, so
relevantAccess != 0
and the App main is skipped.