Bazel: Bazel build crash with IllegalArgumentException

Created on 30 Jan 2019  路  18Comments  路  Source: bazelbuild/bazel

This is bazel 0.22.0, on an otherwise clean machine.

$ bazel build //java/info/adams/self
Starting local Bazel server and connecting to it...
INFO: Invocation ID: d4aa1a5e-eba4-4f08-b033-f974134d23b2
INFO: Analysed target //java/info/adams/self:self (16 packages loaded, 389 targets configured).
INFO: Found 1 target...
ERROR: /home/ulfjack/.cache/bazel/_bazel_ulfjack/d8cef0f170ae4455e05403d45c33884b/external/bazel_tools/tools/jdk/BUILD:188:1: SkylarkAction external/bazel_tools/tools/jdk/platformclasspath.jar failed (Exit 1) java failed: error executing command external/remotejdk_linux/bin/java -XX:+IgnoreUnrecognizedVMOptions '--add-exports=jdk.compiler/com.sun.tools.javac.platform=ALL-UNNAMED' -cp ... (remaining 4 argument(s) skipped)

Use --sandbox_debug to see verbose messages from the sandbox
Exception in thread "main" java.lang.IllegalArgumentException: external/local_jdk
        at jdk.compiler/com.sun.tools.javac.file.Locations$SystemModulesLocationHandler.update(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.file.Locations$SystemModulesLocationHandler.handleOption(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.file.Locations.handleOption(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.file.BaseFileManager.handleOption(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.file.BaseFileManager$2.handleFileManagerOption(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.main.Option.process(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.main.Option.handleOption(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.file.BaseFileManager.handleOption(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.main.Arguments.doProcessArgs(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.main.Arguments.processArgs(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.main.Arguments.init(Unknown Source)
        at jdk.compiler/com.sun.tools.javac.api.JavacTool.getTask(Unknown Source)
        at DumpPlatformClassPath.dumpJDK9AndNewerBootClassPath(DumpPlatformClassPath.java:106)
        at DumpPlatformClassPath.main(DumpPlatformClassPath.java:67)
Target //java/info/adams/self:self failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 36.836s, Critical Path: 3.83s
INFO: 1 process: 1 linux-sandbox.
FAILED: Build did NOT complete successfully
P1 team-Rules-Java bug

Most helpful comment

Tobi and me found the problem, a fix and a workaround. :)

The problem: When JAVA_HOME is set to point to a JRE 8 instead of a JDK, Bazel will happily try to use it, but then chaos unfolds in DumpPlatformClassPath:

1) This method tries to detect whether the target (= system) JDK is a) Java 8 or b) Java >=9: https://source.bazel.build/bazel/+/28841a6c4fe303e661041e479f78ed8197a82548:tools/jdk/DumpPlatformClassPath.java;l=95

2) It does so by making the assumption: If we look for well-known files of OpenJDK 8 and they don't exist, then it must be an OpenJDK >= 9. This check obviously also comes to the conclusion that you're looking at OpenJDK >= 9 when you're not looking at a JDK at all (like a JRE).

3) DumpPlatformClassPath passes the path to its logic for OpenJDK 9 boot classpath detection, which then crashes with an InvalidArgumentException, because it refuses to work with something that's not an OpenJDK >= 9.

The fix: https://github.com/bazelbuild/bazel/pull/7324

The workaround: Set JAVA_HOME to a JDK on platforms where that's not the case, like in the Cloud Shell container. I verified that on Cloud Shell the JAVA_HOME is set incorrectly to a JRE:

philwo@cloudshell:~ (bazel-public)$ echo $JAVA_HOME
/usr/lib/jvm/java-8-openjdk-amd64/jre

By setting it to the JDK (/usr/lib/jvm/java-8-openjdk-amd64) instead, Bazel works fine.

All 18 comments

This seems to be Debian 9.6 (it's a cloud vm, not my personal machine).

@lberki could you take a look/assign to someone who can take a look?

I guess this is the same as #6993 - is there a JDK installed on the machine? We definitely need to improve the error detection there and not crash bazel.

Yes, there is:

$ java -version
openjdk version "1.8.0_181"
OpenJDK Runtime Environment (build 1.8.0_181-8u181-b13-2~deb9u1-b13)
OpenJDK 64-Bit Server VM (build 25.181-b13, mixed mode)

It looks to me like the Java rules are generating an incorrect command line for the DumpPlatformClassPath tool, but it might also be missing error handling there?

Can you please try with --java_toolchain=@bazel_tools//tools/jdk:toolchain_java8?

Same exception.

DumpPlatformClassPath.dumpJDK9AndNewerBootClassPath(DumpPlatformClassPath.java:106)
vs.
openjdk version "1.8.0_181"

馃

/cc @cushon (the earliest I can take a look is tomorrow -- it's very late here already and I'm somewhat exhausted from a day of conferencing)

Well, that's not good. I tried this on a debian:stretch image and I got a _different_ exception:

Internal error thrown during build. Printing stack trace: java.lang.IllegalStateException: //java/a:a BuildConfigurationValue.Key[c4592abc3e3
009cd244247c661481fda] false -> ErrorInfo{exception=com.google.devtools.build.lib.skyframe.FileSymlinkInfiniteExpansionException: Infinite sy
mlink expansion, rootCauses={FILE:[/root/.cache/bazel/_bazel_root/887904812217cca9bc2b9adb875daf42/external/local_jdk]/[.cache]}, cycles=[], 
isCatastrophic=false, rootCauseOfException=FILE:[/root/.cache/bazel/_bazel_root/887904812217cca9bc2b9adb875daf42/external/local_jdk]/[.cache]
, isDirectlyTransient=false, isTransitivelyTransient=false}
        at com.google.common.base.Preconditions.checkState(Preconditions.java:823)
        at com.google.devtools.build.lib.skyframe.SkyframeBuildView.assertSaneAnalysisError(SkyframeBuildView.java:660)
        at com.google.devtools.build.lib.skyframe.SkyframeBuildView.processErrors(SkyframeBuildView.java:525)
        at com.google.devtools.build.lib.skyframe.SkyframeBuildView.configureTargets(SkyframeBuildView.java:444)
        at com.google.devtools.build.lib.analysis.BuildView.update(BuildView.java:369)
        at com.google.devtools.build.lib.buildtool.AnalysisPhaseRunner.runAnalysisPhase(AnalysisPhaseRunner.java:209)
        at com.google.devtools.build.lib.buildtool.AnalysisPhaseRunner.execute(AnalysisPhaseRunner.java:118)
        at com.google.devtools.build.lib.buildtool.BuildTool.buildTargets(BuildTool.java:143)
        at com.google.devtools.build.lib.buildtool.BuildTool.processRequest(BuildTool.java:253)
        at com.google.devtools.build.lib.runtime.commands.BuildCommand.exec(BuildCommand.java:83)
        at com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.execExclusively(BlazeCommandDispatcher.java:477)
        at com.google.devtools.build.lib.runtime.BlazeCommandDispatcher.exec(BlazeCommandDispatcher.java:205)
        at com.google.devtools.build.lib.server.GrpcServerImpl.executeCommand(GrpcServerImpl.java:749)
        at com.google.devtools.build.lib.server.GrpcServerImpl.access$1600(GrpcServerImpl.java:103)
        at com.google.devtools.build.lib.server.GrpcServerImpl$2.lambda$run$0(GrpcServerImpl.java:818)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
        at java.base/java.lang.Thread.run(Unknown Source)

Uh. So turns out, the crash that happens is different when there is no java, _but_ the WORKSPACE is in $HOME. Putting WORKSPACE in a subdirectory reproduces the previous error.

@lfpino : can you take a look?

This appears to happen because DumpPlatformClassPath doesn't like when @local_jdk doesn't actually point to a JDK, for example, when there is no JDK installed locally.

One possible fix is that before DumpPlatformClassPath does anything substantial, it checks whether it actually has a JDK and if not, raises and error. After all, Bazel can't be expected to build Java binaries targeting a nonexistent JDK...

Then, once the fix is there, it's probably patch release time :(

/cc @aehlig

DumpPlatformClassPath.dumpJDK9AndNewerBootClassPath(DumpPlatformClassPath.java:106)
vs.
openjdk version "1.8.0_181"

I think that part is actually OK, the JDK9 in dumpJDK9AndNewerBootClassPath refers to the host JDK, which should is the remote JDK 9.

There's some related discussion of improving diagnostics for missing/incorrect java runtime configuration in #6118,

Investigating.

Tobi and me found the problem, a fix and a workaround. :)

The problem: When JAVA_HOME is set to point to a JRE 8 instead of a JDK, Bazel will happily try to use it, but then chaos unfolds in DumpPlatformClassPath:

1) This method tries to detect whether the target (= system) JDK is a) Java 8 or b) Java >=9: https://source.bazel.build/bazel/+/28841a6c4fe303e661041e479f78ed8197a82548:tools/jdk/DumpPlatformClassPath.java;l=95

2) It does so by making the assumption: If we look for well-known files of OpenJDK 8 and they don't exist, then it must be an OpenJDK >= 9. This check obviously also comes to the conclusion that you're looking at OpenJDK >= 9 when you're not looking at a JDK at all (like a JRE).

3) DumpPlatformClassPath passes the path to its logic for OpenJDK 9 boot classpath detection, which then crashes with an InvalidArgumentException, because it refuses to work with something that's not an OpenJDK >= 9.

The fix: https://github.com/bazelbuild/bazel/pull/7324

The workaround: Set JAVA_HOME to a JDK on platforms where that's not the case, like in the Cloud Shell container. I verified that on Cloud Shell the JAVA_HOME is set incorrectly to a JRE:

philwo@cloudshell:~ (bazel-public)$ echo $JAVA_HOME
/usr/lib/jvm/java-8-openjdk-amd64/jre

By setting it to the JDK (/usr/lib/jvm/java-8-openjdk-amd64) instead, Bazel works fine.

I also sent feedback to the Cloud Shell team so that they are aware of this and can fix the JAVA_HOME in their container. The current situation will probably also cause other build tools like Maven to fail, so fixing this is a good idea anyway. (Internal reference: b/123752456).

Yep, that works! Thanks!

In my case it turns to be missing the jdk at all, so

sudo apt install default-jdk

solved the problem

Was this page helpful?
0 / 5 - 0 ratings