java_toolchain: how can I use the Eclipse compiler in a Bazel build

192 Views Asked by At

I want to migrate an ant build to Bazel 4.2.1. The ant build uses an Eclipse compiler (ecj-3.27.0).

The way to declare a Java compiler in Bazel is java_toolchain.

So I had a look at the output of bazel query @bazel_tools//tools/jdk:all and tried to use the vanilla toolchain as an inspiration (bazelisk query --output=build @bazel_tools//tools/jdk:toolchain_vanilla).

java_toolchain(
  # modified settings
  name = "my-ecj-toolchain", 
  javabuilder = ["@ecj//:ecj.jar"], # my Eclipse compiler jar
  target_version = "8",
  # prevent ecj.jar's error message 'unknown option --persistent_workers' 
  javac_supports_workers = False,
  # prevent ecj.jar's error message 'unknown option --persistent_workers' 
  javac_supports_multiplex_workers = False, 

  # keeping most vanilla-toolchain settings
  bootclasspath = ["@bazel_tools//tools/jdk:platformclasspath"],
  misc = ["-XDskipDuplicateBridges=true", "-XDcompilePolicy=simple", "-g", "-parameters"],
  jvm_opts = [],
  tools = ["@bazel_tools//tools/jdk:javac_jar", "@bazel_tools//tools/jdk:java_compiler_jar", "@bazel_tools//tools/jdk:jdk_compiler_jar"],
  singlejar = ["@bazel_tools//tools/jdk:singlejar"],
  forcibly_disable_header_compilation = True,
  genclass = ["@bazel_tools//tools/jdk:genclass"],
  ijar = ["@bazel_tools//tools/jdk:ijar"],
  header_compiler = ["@bazel_tools//tools/jdk:turbine_direct"],
  header_compiler_direct = ["@bazel_tools//tools/jdk:turbine_direct"],
  jacocorunner = "@bazel_tools//tools/jdk:JacocoCoverageFilegroup",
)

However, it still does not work. ecj.jar complains about the unknown option --output.

bazel aquery MYTARGET displays the whole compiler command line (plus some more build steps):

 Command Line: (exec external/remotejdk11_linux/bin/java \
    -jar \
    external/ecj/ecj.jar \
    --output \
    bazel-out/k8-opt/bin/[...].jar \
    --native_header_output \
    bazel-out/k8-opt/bin/[...]-native-header.jar \
    --output_manifest_proto \
    bazel-out/k8-opt/bin/[...].jar_manifest_proto \
    --compress_jar \
    --output_deps_proto \
    bazel-out/k8-opt/bin/[...].jdeps \
    --bootclasspath \
    bazel-out/k8-opt/bin/external/bazel_tools/tools/jdk/platformclasspath.jar \
    --sources \
    bazel-out/k8-opt/bin/[...].java \
    --javacopts \
    -target \
    8 \
    '-XDskipDuplicateBridges=true' \
    '-XDcompilePolicy=simple' \
    -g \
    -parameters \
    -g \
    -- \
    --target_label \
    bazel-out/k8-opt/bin/[...]:[...] \
    --strict_java_deps \
    ERROR \
    --direct_dependencies \
[...]

I don't know any Java compiler that accepts --output. Do I have to pass ecj.jar in a different way?

2

There are 2 best solutions below

0
On BEST ANSWER

The Eclipse Java Compiler is available as toolchain for Bazel.

https://github.com/salesforce/bazel-jdt-java-toolchain

It wraps ECJ into a builder for Bazel. There are a few limitations. For example it does not support Error Prone yet. However, annotation processing works.

We noticed that ECJ has quite some advantage in our large code base in terms of performance and resource consumption.

2
On

I don't know any Java compiler that accepts --output.

Indeed, Bazel does not invoke OpenJDK's javac executable directly but rather its own wrapper over javac's APIs called buildjar. There is a little about this in the Bazel documentation; buildjar enables some of Bazel's fancier Java features.

So, you would have to provide a buildjar-compatible wrapper for EJC. The closest starting point for such an implementation is likely VanillaJavaBuilder, which is for OpenJDK's javac without any of the invasive API usages in the normal buildjar implementation.