Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--host_javabase option expects a label and is non portable #6012

Closed
davido opened this issue Aug 28, 2018 · 14 comments
Closed

--host_javabase option expects a label and is non portable #6012

davido opened this issue Aug 28, 2018 · 14 comments
Labels
team-Rules-Java Issues for Java rules untriaged

Comments

@davido
Copy link
Contributor

davido commented Aug 28, 2018

I would like to support custom JDK versions (e.g. JDK11) using VanillaJavaBuilder, using default_java_toolchain and leaving the source/target/bootclasspath unset.

To make it work I have to pass custom jdk through a label to --host_javabase, e.g. on Linux:

java_runtime(
    name = "jdk11",
    java_home = "/usr/lib64/jvm/java-11",
    visibility = ["//visibility:public"],
)
filegroup(
    name = "vanillajavabuilder",
    srcs = ["@bazel_tools//tools/jdk:VanillaJavaBuilder_deploy.jar"],
)
default_java_toolchain(
    name = "toolchain_vanilla",
    forcibly_disable_header_compilation = True,
    javabuilder = [":vanillajavabuilder"],
    jvm_opts = [],
)

It could be simplified to set that up and I uploded this PR to expose toolchain_vanilla in core Bazel, but the main problem remains: I have to specify path to /usr/lib64/jvm/java-11 build file, making my toolchain non-portable:

$ bazel build --host_javabase=:jdk11 \
    --host_java_toolchain=//:toolchain_vanilla \
    --java_toolchain=//:toolchain_vanilla \
    :release

So that if on another system JDK11 is located under: /usr/lib/jvm/java-11-oracle the code above wouldn't work.

Reproducer: https://gerrit-review.googlesource.com/c/gerrit/+/194040

@cushon cushon added team-Rules-Java Issues for Java rules untriaged labels Aug 28, 2018
@cushon
Copy link
Contributor

cushon commented Aug 28, 2018

Once Java 11 is supported (#5723) it will be possible to do this with the default toolchain, and won't require setting a custom --host_javabase.

In the interim, you could do something like:

java_runtime(
    name = "absolute_javabase",
    java_home = "$(ABSOLUTE_JAVABASE)",
)

And then build with: --host_javabase=:absolute_javabase --define=ABSOLUTE_JAVABASE=/usr/lib64/jvm/java-11. That keeps the non-portable JDK path out of your BUILD files, at least.

Other options include vendoring a JDK into your repository, or fetching JDK 11 from a remote repository instead of using a locally installed version.

@davido
Copy link
Contributor Author

davido commented Aug 28, 2018

And then build with: --host_javabase=:absolute_javabase --define=ABSOLUTE_JAVABASE=/usr/lib64/jvm/java-11. That keeps the non-portal JDK path out of your BUILD files, at least.

Thanks for the workround. Another non intrusive way to support alternative JDKs would be to teach Bazel to induce the custom JDK from $JAVA_HOME (like other build tools are doing):

  $ JAVA_HOME=/usr/lib64/jvm/java-11 bazel build --some-fancy-option-to-induce-custom-jdk-from-java-home :release

Other options include vendoring a JDK into your repository, [...]

Git is unsuitable for versioning binaries, and network bandwidth is very valuable resources, so that I rule out that option.

@cushon
Copy link
Contributor

cushon commented Aug 28, 2018

teach Bazel to induce the custom JDK from $JAVA_HOME (like other build tools are doing)

We're specifically not doing that by default so the non-VanillaJavaBuilder toolchain can rely on the embedded JDK instead of whatever the local JDK happens to be, to make sure it has a JDK 10. But it's fair to say there are some open questions about the best approach going forward.

@cushon
Copy link
Contributor

cushon commented Sep 14, 2018

To close the loop on this, @bazel_tools//tools/jdk:absolute_javabase is now built-in:

# bazel build --host_javabase=@bazel_tools//tools/jdk:absolute_javabase \
. I think that's the best we can do here.

@cushon cushon closed this as completed Sep 14, 2018
@davido
Copy link
Contributor Author

davido commented Sep 14, 2018

That's nice, thanks.

Given that discussion on repo-discuss mailing list: [1] I'm looking for an option to build Gerrit on stable branches and older tags, where Dagger DI and auto-value still have outdated versions, that are not compatible with JDK9 javac because of removal of javax.annotation.Generated class (what a mess?).

So my question is there an easy way to build gerrit say v2.15 tag with Bazel@HEAD using @bazel_tools//tools/jdk:absolute_javabase? Other options?

To reproduce the problem with Bazel@HEAD (a3ae3e2) checkout v2.15 tag and run:

  $ b9 build gerrit-sshd:sshd
WARNING: The following rc files are no longer being read, please transfer their contents or import their path into one of the standard rc files:
/home/davido/projects/gerrit/tools/bazel.rc
Extracting Bazel installation...
Starting local Bazel server and connecting to it...
INFO: Analysed target //gerrit-sshd:sshd (120 packages loaded).
INFO: Found 1 target...
INFO: From Building gerrit-index/libindex.jar (34 source files) and running annotation processors (AutoAnnotationProcessor, AutoValueProcessor):
warning: An exception occurred while looking for AutoValue extensions. No extensions will function. This may be due to a corrupt jar file in the compiler's classpath. Exception: java.util.ServiceConfigurationError: com.google.auto.value.extension.AutoValueExtension: Provider com.google.auto.value.extension.memoized.MemoizeExtension could not be instantiated
INFO: From Building gerrit-server/libreceive.jar (15 source files) and running annotation processors (AutoAnnotationProcessor, AutoValueProcessor):
warning: An exception occurred while looking for AutoValue extensions. No extensions will function. This may be due to a corrupt jar file in the compiler's classpath. Exception: java.util.ServiceConfigurationError: com.google.auto.value.extension.AutoValueExtension: Provider com.google.auto.value.extension.memoized.MemoizeExtension could not be instantiated
ERROR: /home/davido/projects/gerrit/gerrit-sshd/BUILD:5:1: Building gerrit-sshd/libsshd.jar (97 source files) and running annotation processors (AutoAnnotationProcessor, AutoValueProcessor) failed (Exit 1)
java.lang.RuntimeException: java.lang.NoClassDefFoundError: javax/annotation/Generated
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:158)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:96)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:90)
	at com.google.devtools.build.buildjar.javac.BlazeJavacMain.compile(BlazeJavacMain.java:111)
	at com.google.devtools.build.buildjar.SimpleJavaLibraryBuilder$1.invokeJavac(SimpleJavaLibraryBuilder.java:106)
	at com.google.devtools.build.buildjar.ReducedClasspathJavaLibraryBuilder.compileSources(ReducedClasspathJavaLibraryBuilder.java:54)
	at com.google.devtools.build.buildjar.SimpleJavaLibraryBuilder.compileJavaLibrary(SimpleJavaLibraryBuilder.java:109)
	at com.google.devtools.build.buildjar.SimpleJavaLibraryBuilder.run(SimpleJavaLibraryBuilder.java:117)
	at com.google.devtools.build.buildjar.BazelJavaBuilder.processRequest(BazelJavaBuilder.java:105)
	at com.google.devtools.build.buildjar.BazelJavaBuilder.runPersistentWorker(BazelJavaBuilder.java:67)
	at com.google.devtools.build.buildjar.BazelJavaBuilder.main(BazelJavaBuilder.java:45)
Caused by: java.lang.NoClassDefFoundError: javax/annotation/Generated
	at com.google.auto.value.processor.AutoAnnotationProcessor.getReferencedTypes(AutoAnnotationProcessor.java:443)
	at com.google.auto.value.processor.AutoAnnotationProcessor.processMethod(AutoAnnotationProcessor.java:152)
	at com.google.auto.value.processor.AutoAnnotationProcessor.process(AutoAnnotationProcessor.java:128)
	at com.google.auto.value.processor.AutoAnnotationProcessor.process(AutoAnnotationProcessor.java:112)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:968)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:884)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.access$2200(JavacProcessingEnvironment.java:108)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1206)
	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1315)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1246)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:922)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:100)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:142)
	... 10 more
Caused by: java.lang.ClassNotFoundException: javax.annotation.Generated
	at java.base/java.net.URLClassLoader.findClass(Unknown Source)
	at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
	at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
	... 23 more
Target //gerrit-sshd:sshd failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 53.027s, Critical Path: 42.66s
INFO: 98 processes: 78 linux-sandbox, 20 worker.
FAILED: Build did NOT complete successfully

It worth noting that there are other incompatible changes, like:

WARNING: The following rc files are no longer being read, please transfer their contents or import their path into one of the standard rc files:
/home/davido/projects/gerrit/tools/bazel.rc

So that building of release.war would fail anyway, even if we would find a way to force Bazel to use JDK8 javac and Vanilla Java Builder.

@cushon
Copy link
Contributor

cushon commented Sep 14, 2018

It sounds like #5984 would help here a bit.

@lberki there's a higher-level question about the compatibility guarantees when using the latest Bazel to compile old release branches. It don't think there are any guarantees that will work (at least for pre-1.0 Bazel versions), given: https://groups.google.com/d/topic/bazel-discuss/wYocSoGTJOg/discussion.

The only thing I can think of that would definitely work is to use an old Bazel version corresponding to the version of the code you're using, or even to check the version of Bazel into your repository.

@davido
Copy link
Contributor Author

davido commented Sep 14, 2018

@cushon

It sounds like #5984 would help here a bit.

Yeah exactly! Just figured it out, that with this diff:

diff --git a/BUILD b/BUILD
index 722e240851..810cb296f5 100644
--- a/BUILD
+++ b/BUILD
@@ -3,6 +3,23 @@ package(default_visibility = ["//visibility:public"])
 load("//tools/bzl:genrule2.bzl", "genrule2")
 load("//tools/bzl:pkg_war.bzl", "pkg_war")
 
+load(
+    "@bazel_tools//tools/jdk:default_java_toolchain.bzl",
+    "default_java_toolchain",
+)
+
+filegroup(
+    name = "vanillajavabuilder",
+    srcs = ["@bazel_tools//tools/jdk:VanillaJavaBuilder_deploy.jar"],
+)
+
+default_java_toolchain(
+    name = "toolchain_vanilla",
+    forcibly_disable_header_compilation = True,
+    javabuilder = [":vanillajavabuilder"],
+    jvm_opts = [],
+)
+
 genrule(
     name = "gen_version",
     outs = ["version.txt"],

That is actually the content of my pending PR (that was based on your suggestion actually) and voilá:
Building gerrit v2.15 tag with Vanilla Java Builder and --javabase pointing to Java 8 just works:

$ b9 build --host_javabase=@bazel_tools//tools/jdk:absolute_javabase --define=ABSOLUTE_JAVABASE=/usr/lib64/jvm/java --host_java_toolchain=//:toolchain_vanilla --java_toolchain=//:toolchain_vanilla gerrit-sshd:sshd
WARNING: The following rc files are no longer being read, please transfer their contents or import their path into one of the standard rc files:
/home/davido/projects/gerrit/tools/bazel.rc
INFO: Build options have changed, discarding analysis cache.
INFO: Analysed target //gerrit-sshd:sshd (1 packages loaded).
INFO: Found 1 target...
Target //gerrit-sshd:sshd up-to-date:
  bazel-bin/gerrit-sshd/libsshd.jar
INFO: Elapsed time: 18.279s, Critical Path: 17.79s
INFO: 43 processes: 22 linux-sandbox, 21 worker.
INFO: Build completed successfully, 44 total actions

Unfortunately I still have to patch BUILD to artificially add toolchain_vanilla. I think Bazel's backwards compatibility would benefit from adding toolchain_vanilla as core Bazel feature.

@davido
Copy link
Contributor Author

davido commented Sep 14, 2018

The only thing I can think of that would definitely work is to use an old Bazel version corresponding to the version of the code you're using, or even to check the version of Bazel into your repository.

Another limitation: we cannot install multiple Bazel versions in the moment easily (or am I missing something?): after installing of bazel version foo, bazel version bar gone. We would need multiple docker images, or install different Bazel versions during the CI-build.

@davido
Copy link
Contributor Author

davido commented Sep 20, 2018

@cushon

Temporarily I added this to Gerrit in root BUILD file:

java_runtime(
    name = "absolute_javabase",
    java_home = "$(ABSOLUTE_JAVABASE)",
    visibility = ["//visibility:public"],
)

this caused the CI: [1] to fail with:

Analyzing: 1562 targets (202 packages loaded)
ERROR: /home/jenkins/workspace/Gerrit-verifier-bazel/gerrit/BUILD:26:17: in java_home attribute of java_runtime rule //:absolute_javabase: $(ABSOLUTE_JAVABASE) not defined
ERROR: Analysis of target '//:absolute_javabase' failed; build aborted: Analysis of target '//:absolute_javabase' failed; build aborted

That's because we are building //... on the CI. In fact the CI still uses Java 8.

[1] https://gerrit-ci.gerritforge.com/job/Gerrit-verifier-bazel/54129/consoleText

@cushon
Copy link
Contributor

cushon commented Sep 20, 2018

If the issue is just avoiding building that target when you do build ..., there are a couple of work-arounds:

@davido
Copy link
Contributor Author

davido commented Sep 20, 2018

Thank you! I will upload new CL with tags=["manual"] added.

@davido
Copy link
Contributor Author

davido commented Sep 20, 2018

OK, that didn't work:

//:absolute_javabase: no such attribute 'tag' in 'java_runtime' rule

And vardef doesn't seem to work:

BUILD:24:1: name 'vardef' is not defined

@cushon
Copy link
Contributor

cushon commented Sep 20, 2018

Sorry, vardef doesn't exit, and I'd rather not add java_runtime.tags just for this.

Once you can use the absolute_javabase definition built in to Bazel this problem will go away.

In the interim, I have a hack that works around it by putting the variable reference behind a select:

config_setting(
    name = "use_absolute_javabase",
    values = {"define": "USE_ABSOLUTE_JAVABASE=true"},
)

java_runtime(
    name = "absolute_javabase",
    java_home = select({
        ":use_absolute_javabase": "$(ABSOLUTE_JAVABASE)",
        "//conditions:default": "",
    }), 
    visibility = ["//visibility:public"],
)
bazel build --javabase=:absolute_javabase --define=ABSOLUTE_JAVABASE=<path to JDK> --define=USE_ABSOLUTE_JAVABASE=true ...

@davido
Copy link
Contributor Author

davido commented Sep 20, 2018

Thank you.

openstack-gerrit pushed a commit to openstack-infra/gerrit that referenced this issue Sep 25, 2018
Bazel@HEAD doesn't support Java 10 any more. The tool chain is extended
to support building with Java 10 using host_javabase option and vanilla
java builder. To use --host_javabase option we have to provide absolute
path to the JDK 10. To keep that non-portable part out of the build file
we use variable that is substitued during build invocation. Say, the
location of the JDK 10 is: /usr/lib64/jvm/java-10, then the build is
invoked with:

  $ bazel build --host_javabase=:absolute_javabase \
    --define=ABSOLUTE_JAVABASE=/usr/lib64/jvm/java-10 ...

Given that absolute_javabase rule is not a part of released Bazel yet,
but will be only included in future Bazel releases, we have to add it in
our own build file:

  java_runtime(
      name = "absolute_javabase",
      java_home = "$(ABSOLUTE_JAVABASE)",
      visibility = ["//visibility:public"],
  )

While this works, it fails the Gerrit-CI, because recently it was
changed to exercise //... rule, and cannot resolve ABSOLUTE_JAVABASE
variable, because it wasn't provided in the CI environment. CI is still
using Java 8.

One approach to address that problem would be to use "manual" tag for a
rule that would exclude it from generic targets like //..., but
unfortunately, java_runtime rule doesn't expose tag attribute.

The next workaround is to hide that variable behind a select statement.
This is a harmless hack:

  config_setting(
      name = "use_absolute_javabase",
      values = {"define": "USE_ABSOLUTE_JAVABASE=true"},
  )

  java_runtime(
      name = "absolute_javabase",
      java_home = select({
          ":use_absolute_javabase": "$(ABSOLUTE_JAVABASE)",
          "//conditions:default": "",
      }),
      visibility = ["//visibility:public"],
  )

Now from the CI the rule: //:absolute_javabase would produce non working
java_runtime, because java_home wasn't specified, but given that this
java_runtime is not used during the build, it doesn't matter. Add also
a TODO comment to replace that interim solution when absolute_javabase
is supported in released Bazel version. As the consequence for hiding
that rule behind a condition, we need to provide additional variable so
that Bazel can correctly resolve java_home in java_runtime rule:

  $ bazel build --host_javabase=:absolute_javabase \
    --define=ABSOLUTE_JAVABASE=/usr/lib64/jvm/java-10 \
    --define=USE_ABSOLUTE_JAVABASE=true [...]

Also extend tools/eclipse/project.py to accept Java 10 specific options.
There is already --java <jdk number> option that was added recently to
support Eclipse .classpath generation when JDK 9 is used, but this can
not be used, because for Java 10 we have to provide absolute path to the
Java 10 location. Add new option --edge_java where all Java 10 specific
options can be passed:

  $ tools/eclipse/project.py --edge_java \
    "--host_javabase=:absolute_javabase \
    --define=ABSOLUTE_JAVABASE=/usr/lib64/jvm/java-10 \
    --define=USE_ABSOLUTE_JAVABASE=true \
    --host_java_toolchain=//:toolchain_vanilla \
    --java_toolchain=//:toolchain_vanilla"

Alternative approach would be to add those options to Bazel resource
file.

Test Plan:

  $ bazel build --host_javabase=:absolute_javabase \
    --define=ABSOLUTE_JAVABASE=/usr/lib64/jvm/java-10 \
    --define=USE_ABSOLUTE_JAVABASE=true \
    --host_java_toolchain=//:toolchain_vanilla \
    --java_toolchain=//:toolchain_vanilla \
    :release

and replace ABSOLUTE_JAVABASE variable with the location of JDK 10.

To verify that major version of generated classes is 54, use:

  $ $JAVA_HOME/bin/javap -verbose \
    -cp bazel-bin/java/com/google/gerrit/common/libserver.jar \
     com.google.gerrit.common.data.ProjectAccess | grep "major version"
  54

[1] bazelbuild/bazel#6012
Change-Id: I5e652f7a19afbcf3607febd9a7a165dc07918bd0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
team-Rules-Java Issues for Java rules untriaged
Projects
None yet
Development

No branches or pull requests

2 participants