bazelbuild / bazel

a fast, scalable, multi-language and extensible build system
https://bazel.build
Apache License 2.0
23.2k stars 4.06k forks source link

Java 11 doesn't work with Android builds #8615

Open cushon opened 5 years ago

cushon commented 5 years ago

Using Bazel at head (4aa48f9d83ae3c34ceebb172dc4479607cc54fbc) and configuring the Java 11 language level with --javacopt='--release 11' (as currently suggested in https://github.com/bazelbuild/bazel/issues/6245#issuecomment-494879374) doesn't allow building Android targets.

Repro:

git clone git@github.com:bazelbuild/examples.git bazel-examples
cd bazel-examples/android/tutorial

Create a minimal WORKSPACE and BUILD:

git diff HEAD
diff --git a/android/tutorial/WORKSPACE b/android/tutorial/WORKSPACE
new file mode 100644
index 0000000..9cc43bb
--- /dev/null
+++ b/android/tutorial/WORKSPACE
@@ -0,0 +1 @@
+android_sdk_repository(name = "androidsdk")
diff --git a/android/tutorial/src/main/java/com/example/bazel/BUILD b/android/tutorial/src/main/java/com/example/bazel/BUILD
new file mode 100644
index 0000000..76f8ba7
--- /dev/null
+++ b/android/tutorial/src/main/java/com/example/bazel/BUILD
@@ -0,0 +1,13 @@
+package(
+    default_visibility = ["//src:__subpackages__"],
+)
+
+android_library(
+    name = "greeter_activity",
+    srcs = [
+        "Greeter.java",
+        "MainActivity.java",
+    ],
+    manifest = "AndroidManifest.xml",
+    resource_files = glob(["res/**"]),
+)

Building with default options works:

$ bazel build ...
...
Target //src/main/java/com/example/bazel:greeter_activity up-to-date:

Building with --javacopt='--release 11' doesn't:

$ bazel build --javacopt='--release 11' ...
...
ERROR: bazel-examples/android/tutorial/src/main/java/com/example/bazel/BUILD:5:1: Building src/main/java/com/example/bazel/libgreeter_activity.jar (2 source files) failed (Exit 1)
src/main/java/com/example/bazel/MainActivity.java:3: error: package android.app does not exist
import android.app.Activity;
                  ^

Setting --javacopt='-verbose --release 11' provides some hints about what's going on:

$ bazel build --javacopt='-verbose --release 11' ...
...
[search path for class files: bazel-out/host/bin/external/bazel_tools/tools/android/java_lang_extras.jar,external/androidsdk/platforms/android-28/android.jar,bazel-out/k8-fastbuild/bin/src/main/java/com/example/bazel/greeter_activity_resources.jar]
...
[loading modules/java.base/java/lang/Object.class]

The search path for class files shows that the expected bootclasspath jars are being passed to javac, including android.jar. But the [loading modules/java.base/java/lang/Object.class] suggests that when targeting a language level that supports modules (i.e. Java >= 9), the compiler is ignoring the legacy bootclasspath configuration and looking at the host/exec JDK's module equivalent.

That's supported by the javac docs:

--boot-class-path path or -bootclasspath path Overrides the location of the bootstrap class files. This can only be used when compiling for versions prior to JDK 9. As applicable, see the descriptions in --release, -source, or -target for details.

The --system flag can be used to support this kind of cross compilation, but the argument is a path to a complete JDK containing the system APIs we're targeting, which we don't have for android.jar.

iirina commented 5 years ago

cc @jin

jin commented 5 years ago

cc @ahumesky

sgammon commented 4 years ago

this is a blocker for us because we're building in a Micronaut codebase on Java 12. is there any way the community can help?

ThomasCJY commented 3 years ago

Any update on this issue? This has become a blocker for us to bump java version to 11

mauriciogg commented 3 years ago

Looks like this gradle change needs to be replicated in bazel: https://cs.android.com/android-studio/platform/tools/base/+/79330f946ba270218ef06e34dfaed22c759c0613

Which is just unpacking an core-for-system-modules.jar with the sdk (30+) and passing the android jar in the classpath instead of bootclasspath. The core-for-system-modules.jar dependency makes it so that this only works for api 30 and above (not entirely sure why classpath is not enough though).

cushon commented 3 years ago

We laid most of the groundwork for this with java_common.BootClassPathInfo. That provider can be used with java_toolchain.bootclasspath to set the contents of a 'system' directory that's passed to javac's --system flag.

--system overrides the system APIs for Java > 9 compilations, in the way that -bootclasspath did for Java <= 8 compilations.

I think most of what's needed here is to create a system directory containing the classes in android.jar, and then to configure the java_toolchain for Android compilations to use it.

cushon commented 3 years ago

To expand a little, this doesn't require changes to Bazel core, it 'just' requires creating a starlark rule that packages up android.jar and provides java_common.BootClassPathInfo, and then to use that rule in the bootclasspath attribute of the configured java_toolchain.

e.g. something like

java_toolchain(
   ...
   bootclasspath = [":android_system"]
)
dhbaird commented 2 years ago

Using local JDK seems to work. But I need remote JDK to work, and to get any version between up to 13 working (a restriction per proguard 6.2.2), which leads me to this issue. Any practical info that could help me? I'm already building bazel from source in order to work around another issue (https://github.com/bazelbuild/bazel/issues/13989), so I can edit anything needed to do so. I'm working off of release-6.0.0-pre.20220502.3rc1. Thanks in advance.

jvliwanag commented 11 months ago

I needed to build an android_library with a maven dependency on a classes on Java 17. My workaround was to use kt_android_library which worked even for just plain java sources.