diff options
Diffstat (limited to 'rules/java')
26 files changed, 1938 insertions, 56 deletions
diff --git a/rules/java/BUILD b/rules/java/BUILD index e69de29b..763713c9 100644 --- a/rules/java/BUILD +++ b/rules/java/BUILD @@ -0,0 +1,112 @@ +""" +Copyright (C) 2023 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +load(":merged_txts_test.bzl", "merged_txts_test_suite") +load(":sdk_library_test.bzl", "java_sdk_library_test_suite") +load(":java_system_modules_test.bzl", "java_system_modules_test_suite") +load(":bootclasspath_test.bzl", "bootclasspath_test_suite") +load(":versions_test.bzl", "versions_test_suite") +load(":versions.bzl", "java_versions") +load(":sdk_transition_test.bzl", "sdk_transition_test_suite") +load(":host_for_device_test.bzl", "host_for_device_test_suite") +load("@bazel_skylib//rules:common_settings.bzl", "string_setting") +load("@bazel_tools//tools/jdk:default_java_toolchain.bzl", "DEFAULT_JAVACOPTS", "default_java_toolchain") +load("@soong_injection//java_toolchain:constants.bzl", "constants") + +package( + default_visibility = ["//visibility:public"], +) + +java_sdk_library_test_suite(name = "java_sdk_library_tests") + +merged_txts_test_suite(name = "merged_txts_tests") + +java_system_modules_test_suite(name = "java_system_modules_tests") + +bootclasspath_test_suite(name = "bootclasspath_tests") + +versions_test_suite(name = "versions_tests") + +sdk_transition_test_suite(name = "sdk_transition_tests") + +host_for_device_test_suite(name = "host_for_device_test_suite") + +string_setting( + name = "version", + build_setting_default = str(java_versions.get_version()), + values = [str(v) for v in java_versions.ALL_VERSIONS], +) + +[ + config_setting( + name = setting, + flag_values = { + "//build/bazel/rules/java:version": str(java_version), + }, + ) + for java_version, setting in java_versions.VERSION_TO_CONFIG_SETTING.items() +] + +# There is no need for both host and device java version build settings in a +# world where every java_*/android_*/kt_* target uses the AOSP-specific +# wrappers. However, there are targets defined by BUILD.tools files within the +# Bazel binary that do not use the wrapper. These would inherit their java +# version from their reverse dependency, which can cause build failures (e.g. an +# android_library_import with java_version=7 has a tools dependency on a +# non-wrapped Bazel java_library that uses lambdas). By using a separate host +# version, we can reset it to its default when in the device configuration, so +# that a subsequent exec transition will use the default java version. +string_setting( + name = "host_version", + build_setting_default = str(java_versions.get_version()), + values = [str(v) for v in java_versions.ALL_VERSIONS], +) + +[ + config_setting( + name = "host_" + setting, + flag_values = { + "//build/bazel/rules/java:host_version": str(java_version), + }, + ) + for java_version, setting in java_versions.VERSION_TO_CONFIG_SETTING.items() +] + +java_version_select_dict = { + "host_" + setting: str(version) + for version, setting in java_versions.VERSION_TO_CONFIG_SETTING.items() +} | { + "//conditions:default": str(java_versions.get_version()), +} + +default_java_toolchain( + name = "jdk17_host_toolchain_java", + # TODO(b/218720643): Support switching between multiple JDKs. + java_runtime = "//prebuilts/jdk/jdk17:jdk17_runtime", + misc = DEFAULT_JAVACOPTS + constants.CommonJdkFlags, + source_version = select(java_version_select_dict), + target_version = select(java_version_select_dict), + toolchain_definition = False, +) + +toolchain( + name = "jdk17_host_toolchain_java_definition", + exec_compatible_with = ["//build/bazel/platforms/os:linux"], + target_compatible_with = ["//build/bazel/platforms/os:linux"], + target_settings = [], + toolchain = ":jdk17_host_toolchain_java", + toolchain_type = "@bazel_tools//tools/jdk:toolchain_type", +) diff --git a/rules/java/bootclasspath.bzl b/rules/java/bootclasspath.bzl new file mode 100644 index 00000000..78fddcfb --- /dev/null +++ b/rules/java/bootclasspath.bzl @@ -0,0 +1,49 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +load(":java_system_modules.bzl", "SystemInfo") + +def _bootclasspath_impl(ctx): + compile_jars = lambda b: b[JavaInfo].compile_jars.to_list() + return java_common.BootClassPathInfo( + bootclasspath = [jar for b in ctx.attr.bootclasspath for jar in compile_jars(b)], + system = ctx.attr.system[SystemInfo].system if ctx.attr.system else None, + auxiliary = [jar for b in ctx.attr.auxiliary for jar in compile_jars(b)], + ) + +bootclasspath = rule( + implementation = _bootclasspath_impl, + attrs = { + "bootclasspath": attr.label_list( + providers = [JavaInfo], + doc = "The list of libraries to use as javac's --bootclasspath argument.", + ), + "system": attr.label( + providers = [SystemInfo], + doc = "The java_system_modules target to use as javac's --system argument.", + ), + "auxiliary": attr.label_list( + providers = [JavaInfo], + doc = "The list of libraries to include first in javac's --classpath.", + ), + }, + provides = [java_common.BootClassPathInfo], + doc = """Provides BootClassPathInfo to a Java toolchain. + +the java_common.BootClassPathInfo provider is used by a Java toolchain to +set javac's --bootclasspath and --system arguments. It can also optionally add +to the classpath before anything else gets added to it. This rule generates this +provider from a list of JavaInfo-providing targets for --bootclasspath and +--classpath, and from a single SystemInfo-providing target for --system. +""", +) diff --git a/rules/java/bootclasspath_test.bzl b/rules/java/bootclasspath_test.bzl new file mode 100644 index 00000000..652acf2a --- /dev/null +++ b/rules/java/bootclasspath_test.bzl @@ -0,0 +1,67 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load(":bootclasspath.bzl", "bootclasspath") +load(":rules.bzl", "java_import") +load(":java_system_modules.bzl", "java_system_modules") + +def _bootclasspath_test_impl(ctx): + env = analysistest.begin(ctx) + bootclasspath_target = analysistest.target_under_test(env) + + asserts.true( + env, + java_common.BootClassPathInfo in bootclasspath_target, + "Expected BootClassPathInfo in bootclasspath providers.", + ) + return analysistest.end(env) + +bootclasspath_test = analysistest.make( + _bootclasspath_test_impl, +) + +def test_bootclasspath_provider(): + name = "test_bootclasspath_provider" + import_target = ":" + name + "_import" + system_target = ":" + name + "_jsm" + bootclasspath( + name = name + "_target", + bootclasspath = [import_target], + system = system_target, + auxiliary = [import_target], + tags = ["manual"], + ) + bootclasspath_test( + name = name, + target_under_test = name + "_target", + ) + java_system_modules( + name = name + "_jsm", + deps = [import_target], + tags = ["manual"], + ) + java_import( + name = import_target[1:], + jars = ["some_jar.jar"], + tags = ["manual"], + ) + return name + +def bootclasspath_test_suite(name): + native.test_suite( + name = name, + tests = [ + test_bootclasspath_provider(), + ], + ) diff --git a/rules/java/event_log_tags.bzl b/rules/java/event_log_tags.bzl new file mode 100644 index 00000000..b802258b --- /dev/null +++ b/rules/java/event_log_tags.bzl @@ -0,0 +1,49 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Event log tags generation rule""" + +load("@bazel_skylib//lib:paths.bzl", "paths") + +def _event_log_tags_impl(ctx): + out_files = [] + for logtag_file in ctx.files.srcs: + out_filename = paths.replace_extension(logtag_file.basename, ".java") + out_file = ctx.actions.declare_file(out_filename) + out_files.append(out_file) + ctx.actions.run( + inputs = [logtag_file], + outputs = [out_file], + arguments = [ + "-o", + out_file.path, + logtag_file.path, + ], + progress_message = "Generating Java logtag file from %s" % logtag_file.short_path, + executable = ctx.executable._logtag_to_java_tool, + ) + return [DefaultInfo(files = depset(out_files))] + +event_log_tags = rule( + implementation = _event_log_tags_impl, + attrs = { + "srcs": attr.label_list(allow_files = [".logtags"], mandatory = True), + "_logtag_to_java_tool": attr.label( + executable = True, + cfg = "exec", + allow_files = True, + default = Label("//build/make/tools:java-event-log-tags"), + ), + }, +) diff --git a/rules/java/host_for_device.bzl b/rules/java/host_for_device.bzl new file mode 100644 index 00000000..9d7990a1 --- /dev/null +++ b/rules/java/host_for_device.bzl @@ -0,0 +1,40 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +visibility([ + "//external/guava/...", + "//external/kotlinx.coroutines/...", + "//external/robolectric-shadows/...", + "//external/robolectric/...", +]) + +def _host_for_device_impl(ctx): + return [java_common.merge([d[JavaInfo] for d in ctx.attr.exports])] + +java_host_for_device = rule( + doc = """Rule to provide java libraries built with a host classpath in a device configuration. +This is rarely necessary and restricted to a few allowed projects. +""", + implementation = _host_for_device_impl, + attrs = { + # This attribute must have a specific name to let the DexArchiveAspect propagate + # through it. + "exports": attr.label_list( + cfg = "exec", + providers = [JavaInfo], + doc = "List of targets whose contents will be visible to targets that depend on this target.", + ), + }, + provides = [JavaInfo], +) diff --git a/rules/java/host_for_device_test.bzl b/rules/java/host_for_device_test.bzl new file mode 100644 index 00000000..5585bad9 --- /dev/null +++ b/rules/java/host_for_device_test.bzl @@ -0,0 +1,78 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load(":host_for_device.bzl", "java_host_for_device") +load(":rules.bzl", "java_import") +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") + +Platform = provider( + "Platform of the leaf dependency in a linear dependency chain", + fields = { + "platform": "the target platform", + "host_platform": "the host platform", + }, +) + +def _host_for_device_tester_aspect_impl(target, ctx): + if ctx.rule.attr.exports and len(ctx.rule.attr.exports) > 0 and Platform in ctx.rule.attr.exports[0]: + return ctx.rule.attr.exports[0][Platform] + return Platform( + platform = ctx.fragments.platform.platform, + host_platform = ctx.fragments.platform.host_platform, + ) + +host_for_device_tester_aspect = aspect( + implementation = _host_for_device_tester_aspect_impl, + attr_aspects = ["exports"], + fragments = ["platform"], + provides = [Platform], +) + +def _host_for_device_dep_runs_in_exec_config_test_impl(ctx): + env = analysistest.begin(ctx) + target_under_test = analysistest.target_under_test(env) + actual_platform = target_under_test[Platform].platform + expected_platform = target_under_test[Platform].host_platform + asserts.equals(env, expected_platform, actual_platform) + asserts.true(env, JavaInfo in target_under_test, "Expected host_for_device to provide JavaInfo") + return analysistest.end(env) + +host_for_device_dep_runs_in_exec_config_test = analysistest.make( + _host_for_device_dep_runs_in_exec_config_test_impl, + extra_target_under_test_aspects = [host_for_device_tester_aspect], +) + +def test_host_for_device(name): + java_host_for_device( + name = name + "_parent", + exports = [name + "_child"], + tags = ["manual"], + ) + java_import( + name = name + "_child", + jars = ["blah.jar"], + tags = ["manual"], + ) + host_for_device_dep_runs_in_exec_config_test( + name = name, + target_under_test = name + "_parent", + ) + return name + +def host_for_device_test_suite(name): + native.test_suite( + name = name, + tests = [test_host_for_device("test_host_for_device")], + ) diff --git a/rules/java/import.bzl b/rules/java/import.bzl new file mode 100644 index 00000000..6770c5f0 --- /dev/null +++ b/rules/java/import.bzl @@ -0,0 +1,67 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Macro wrapping the java_import for bp2build. """ + +load("@rules_java//java:defs.bzl", _java_import = "java_import") +load("//build/bazel/rules/java:sdk_transition.bzl", "sdk_transition", "sdk_transition_attrs") + +# TODO(b/277801336): document these attributes. +def java_import( + name = "", + jars = [], + deps = [], + tags = [], + target_compatible_with = [], + visibility = None, + **kwargs): + lib_name = name + "_private" + _java_import( + name = lib_name, + jars = jars, + deps = deps, + tags = tags + ["manual"], + target_compatible_with = target_compatible_with, + visibility = ["//visibility:private"], + **kwargs + ) + + java_import_sdk_transition( + name = name, + sdk_version = "none", + java_version = None, + exports = lib_name, + tags = tags, + target_compatible_with = target_compatible_with, + visibility = visibility, + ) + +# The list of providers to forward was determined using cquery on one +# of the example targets listed under EXAMPLE_WRAPPER_TARGETS at +# //build/bazel/ci/target_lists.sh. It may not be exhaustive. A unit +# test ensures that the wrapper's providers and the wrapped rule's do +# match. +def _java_import_sdk_transition_impl(ctx): + return [ + ctx.attr.exports[0][JavaInfo], + ctx.attr.exports[0][ProguardSpecProvider], + ctx.attr.exports[0][OutputGroupInfo], + ctx.attr.exports[0][DefaultInfo], + ] + +java_import_sdk_transition = rule( + implementation = _java_import_sdk_transition_impl, + attrs = sdk_transition_attrs, + provides = [JavaInfo], +) diff --git a/rules/java/java_aidl_library.bzl b/rules/java/java_aidl_library.bzl new file mode 100644 index 00000000..94fd8c5c --- /dev/null +++ b/rules/java/java_aidl_library.bzl @@ -0,0 +1,91 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/bazel/rules/aidl:aidl_library.bzl", "AidlGenInfo", "aidl_file_utils") +load("//build/bazel/rules/java:sdk_transition.bzl", "sdk_transition") + +JavaAidlAspectInfo = provider("JavaAidlAspectInfo", fields = ["jars"]) + +def _java_aidl_gen_aspect_impl(target, ctx): + aidl_gen_java_files = aidl_file_utils.generate_aidl_bindings(ctx, "java", target[AidlGenInfo]) + java_deps = [ + d[JavaInfo] + for d in ctx.rule.attr.deps + ] + out_jar = ctx.actions.declare_file(target.label.name + "-aidl-gen.jar") + java_info = java_common.compile( + ctx, + source_files = aidl_gen_java_files, + deps = java_deps, + output = out_jar, + java_toolchain = ctx.toolchains["@bazel_tools//tools/jdk:toolchain_type"].java, + ) + + return [ + java_info, + JavaAidlAspectInfo( + jars = depset([out_jar]), + ), + ] + +_java_aidl_gen_aspect = aspect( + implementation = _java_aidl_gen_aspect_impl, + attr_aspects = ["deps"], + attrs = { + "_aidl_tool": attr.label( + allow_files = True, + executable = True, + cfg = "exec", + default = Label("//prebuilts/build-tools:linux-x86/bin/aidl"), + ), + }, + toolchains = ["@bazel_tools//tools/jdk:toolchain_type"], + fragments = ["java"], + provides = [JavaInfo, JavaAidlAspectInfo], +) + +def _java_aidl_library_rule_impl(ctx): + java_info = java_common.merge([d[JavaInfo] for d in ctx.attr.deps]) + runtime_jars = depset(transitive = [dep[JavaAidlAspectInfo].jars for dep in ctx.attr.deps]) + transitive_runtime_jars = depset(transitive = [java_info.transitive_runtime_jars]) + + return [ + java_info, + DefaultInfo( + files = runtime_jars, + runfiles = ctx.runfiles(transitive_files = transitive_runtime_jars), + ), + OutputGroupInfo(default = depset()), + ] + +java_aidl_library = rule( + implementation = _java_aidl_library_rule_impl, + attrs = { + # This attribute's name lets the DexArchiveAspect propagate + # through it. It should be changed carefully. + "deps": attr.label_list( + providers = [AidlGenInfo], + aspects = [_java_aidl_gen_aspect], + cfg = sdk_transition, + ), + "java_version": attr.string(), + "sdk_version": attr.string( + default = "system_current", + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, + provides = [JavaInfo], +) diff --git a/rules/java/java_system_modules.bzl b/rules/java/java_system_modules.bzl new file mode 100644 index 00000000..41e76a83 --- /dev/null +++ b/rules/java/java_system_modules.bzl @@ -0,0 +1,177 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +load("@bazel_skylib//lib:paths.bzl", "paths") + +SystemInfo = provider(fields = ["system"]) + +def _gen_module_info_java(ctx, jars_to_module_info, jars, module_info): + ctx.actions.run_shell( + inputs = jars, + outputs = [module_info], + command = "{} java.base {} > {}".format( + jars_to_module_info.path, + " ".join([jar.path for jar in jars]), + module_info.path, + ), + tools = [jars_to_module_info], + ) + +def _gen_module_info_class(ctx, java_runtime, module_info, java_base_patch_jars, module_info_class): + ctx.actions.run_shell( + inputs = depset([module_info], transitive = [java_base_patch_jars]), + outputs = [module_info_class], + tools = java_runtime.files, + command = "{} -d {} --system=none --patch-module=java.base={} {}".format( + paths.join(java_runtime.java_home, "bin", "javac"), + module_info_class.dirname, + ":".join([jar.path for jar in java_base_patch_jars.to_list()]), + module_info.path, + ), + ) + +def _gen_module_info_jar(ctx, soong_zip, module_info_class, module_info_jar): + args = ctx.actions.args() + args.add("-jar") + args.add("--symlinks=false") + args.add("-o", module_info_jar) + args.add("-C", module_info_class.dirname) + args.add("-f", module_info_class) + ctx.actions.run( + inputs = [module_info_class], + outputs = [module_info_jar], + arguments = [args], + executable = soong_zip, + ) + +def _gen_merged_module_jar(ctx, merge_zips, module_info_jar, jars, merged_module_jar): + args = ctx.actions.args() + args.add("-j", merged_module_jar) + args.add_all(depset([module_info_jar], transitive = [jars])) + ctx.actions.run( + inputs = depset([module_info_jar], transitive = [jars]), + outputs = [merged_module_jar], + arguments = [args], + executable = merge_zips, + ) + +def _gen_jmod(ctx, java_runtime, merged_module_jar, jmod): + ctx.actions.run_shell( + inputs = [merged_module_jar], + outputs = [jmod], + tools = java_runtime.files, + command = ( + "{} create --module-version $({} --version) " + + "--target-platform android --class-path {} {}" + ).format( + paths.join(java_runtime.java_home, "bin", "jmod"), + paths.join(java_runtime.java_home, "bin", "jlink"), + merged_module_jar.path, + jmod.path, + ), + ) + +def _gen_system(ctx, java_runtime, jmod, system): + ctx.actions.run_shell( + inputs = depset([jmod], transitive = [java_runtime.files]), + outputs = [system], + tools = java_runtime.files, + command = ( + "rm -rf {} && " + + "{} --module-path {} --add-modules java.base --output {} " + + "--disable-plugin system-modules && " + + "cp {} {}/lib/" + ).format( + system.path, + paths.join(java_runtime.java_home, "bin", "jlink"), + jmod.dirname, + system.path, + paths.join(java_runtime.java_home, "lib", "jrt-fs.jar"), + system.path, + ), + ) + +def _java_system_modules_impl(ctx): + java_info = java_common.merge([d[JavaInfo] for d in ctx.attr.deps]) + module_info = ctx.actions.declare_file("%s/src/module-info.java" % ctx.label.name) + _gen_module_info_java(ctx, ctx.executable._jars_to_module_info, java_info.compile_jars.to_list(), module_info) + + java_runtime = ctx.attr._runtime[java_common.JavaRuntimeInfo] + module_info_class = ctx.actions.declare_file("%s/class/module-info.class" % ctx.label.name) + _gen_module_info_class(ctx, java_runtime, module_info, java_info.compile_jars, module_info_class) + + module_info_jar = ctx.actions.declare_file("%s/jar/classes.jar" % ctx.label.name) + _gen_module_info_jar(ctx, ctx.executable._soong_zip, module_info_class, module_info_jar) + + merged_module_jar = ctx.actions.declare_file("%s/merged/module.jar" % ctx.label.name) + _gen_merged_module_jar( + ctx, + ctx.executable._merge_zips, + module_info_jar, + java_info.full_compile_jars, + merged_module_jar, + ) + + jmod = ctx.actions.declare_file("%s/jmod/java.base.jmod" % ctx.label.name) + _gen_jmod(ctx, java_runtime, merged_module_jar, jmod) + + system = ctx.actions.declare_directory("%s/system" % ctx.label.name) + _gen_system(ctx, java_runtime, jmod, system) + + return [ + SystemInfo( + system = system, + ), + DefaultInfo(files = depset([system])), + ] + +java_system_modules = rule( + implementation = _java_system_modules_impl, + attrs = { + "_jars_to_module_info": attr.label( + allow_files = True, + executable = True, + cfg = "exec", + default = "//build/soong/scripts:jars-to-module-info-java", + ), + "_soong_zip": attr.label( + cfg = "exec", + allow_single_file = True, + doc = "The tool soong_zip", + default = "//build/soong/zip/cmd:soong_zip", + executable = True, + ), + "_merge_zips": attr.label( + cfg = "exec", + allow_single_file = True, + doc = "The tool merge_zips.", + default = "//prebuilts/build-tools:linux-x86/bin/merge_zips", + executable = True, + ), + "_runtime": attr.label( + default = Label("@bazel_tools//tools/jdk:current_java_runtime"), + cfg = "exec", + providers = [java_common.JavaRuntimeInfo], + ), + "deps": attr.label_list( + providers = [JavaInfo], + doc = "Libraries to be converted into a system module directory structure.", + ), + }, + doc = """Generates a system module directory from Java libraries. + +Starting from version 1.9, Java requires a subset of java.* classes to be +provided via system modules. This rule encapsulates the set of steps necessary +to convert a jar file into the directory structure of system modules. +""", +) diff --git a/rules/java/java_system_modules_test.bzl b/rules/java/java_system_modules_test.bzl new file mode 100644 index 00000000..3b76f4c8 --- /dev/null +++ b/rules/java/java_system_modules_test.bzl @@ -0,0 +1,59 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load(":java_system_modules.bzl", "SystemInfo", "java_system_modules") +load(":rules.bzl", "java_import") + +def _java_system_modules_test_impl(ctx): + env = analysistest.begin(ctx) + java_system_modules_target = analysistest.target_under_test(env) + + asserts.true( + env, + java_system_modules_target[SystemInfo].system.is_directory, + "java_system_modules output should be a directory.", + ) + return analysistest.end(env) + +java_system_modules_test = analysistest.make( + _java_system_modules_test_impl, +) + +def test_java_system_modules_provider(): + name = "test_java_system_modules_provider" + import_target = ":" + name + "_import" + java_system_modules( + name = name + "_target", + deps = [import_target], + tags = ["manual"], + ) + java_system_modules_test( + name = name, + target_under_test = name + "_target", + ) + + java_import( + name = import_target[1:], + jars = ["some_jar.jar"], + tags = ["manual"], + ) + return name + +def java_system_modules_test_suite(name): + native.test_suite( + name = name, + tests = [ + test_java_system_modules_provider(), + ], + ) diff --git a/rules/java/library.bzl b/rules/java/library.bzl index f45cd51b..f381b1c2 100644 --- a/rules/java/library.bzl +++ b/rules/java/library.bzl @@ -1,8 +1,80 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Macro wrapping the java_library for bp2build. """ -def java_library(name = "", srcs = [], deps = [], javacopts = [], **kwargs): - # Disable the error prone check of HashtableContains by default. See https://errorprone.info/bugpattern/HashtableContains - # HashtableContains error is reported when compiling //external/bouncycastle:bouncycastle-bcpkix-unbundled +load( + "@rules_java//java:defs.bzl", + _java_library = "java_library", +) +load("//build/bazel/rules/java:sdk_transition.bzl", "sdk_transition", "sdk_transition_attrs") + +# TODO(b/277801336): document these attributes. +def java_library( + name = "", + srcs = [], + deps = [], + javacopts = [], + sdk_version = None, + java_version = None, + tags = [], + target_compatible_with = [], + visibility = None, + **kwargs): + lib_name = name + "_private" + + # Disable the error prone check of HashtableContains by default. See https://errorprone.info/bugpattern/HashtableContains + # HashtableContains error is reported when compiling //external/bouncycastle:bouncycastle-bcpkix-unbundled opts = ["-Xep:HashtableContains:OFF"] + javacopts - native.java_library(name, srcs = srcs, deps = deps, javacopts = opts, **kwargs) + _java_library( + name = lib_name, + srcs = srcs, + deps = deps, + javacopts = opts, + tags = tags + ["manual"], + target_compatible_with = target_compatible_with, + visibility = ["//visibility:private"], + **kwargs + ) + + java_library_sdk_transition( + name = name, + sdk_version = sdk_version, + java_version = java_version, + exports = lib_name, + tags = tags, + target_compatible_with = target_compatible_with, + visibility = ["//visibility:public"], + ) + +# The list of providers to forward was determined using cquery on one +# of the example targets listed under EXAMPLE_WRAPPER_TARGETS at +# //build/bazel/ci/target_lists.sh. It may not be exhaustive. A unit +# test ensures that the wrapper's providers and the wrapped rule's do +# match. +def _java_library_sdk_transition_impl(ctx): + return [ + ctx.attr.exports[0][JavaInfo], + ctx.attr.exports[0][InstrumentedFilesInfo], + ctx.attr.exports[0][ProguardSpecProvider], + ctx.attr.exports[0][OutputGroupInfo], + ctx.attr.exports[0][DefaultInfo], + ] + +java_library_sdk_transition = rule( + implementation = _java_library_sdk_transition_impl, + attrs = sdk_transition_attrs, + provides = [JavaInfo], +) diff --git a/rules/java/merged_txts.bzl b/rules/java/merged_txts.bzl new file mode 100644 index 00000000..069903f8 --- /dev/null +++ b/rules/java/merged_txts.bzl @@ -0,0 +1,84 @@ +""" +Copyright (C) 2023 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +load("//build/bazel/rules/java:sdk_library.bzl", "JavaSdkLibraryInfo") + +METALAVA_ARGS = [ + "-J--add-opens=java.base/java.util=ALL-UNNAMED", + "--quiet", + "--no-banner", + "--format=v2", +] + +def _get_inputs(ctx): + inputs = [] + inputs.extend(ctx.files.base) + from_deps = [] + if ctx.attr.scope == "public": + from_deps = [d[JavaSdkLibraryInfo].public for d in ctx.attr.deps] + elif ctx.attr.scope == "system": + from_deps = [d[JavaSdkLibraryInfo].system for d in ctx.attr.deps] + elif ctx.attr.scope == "module-lib": + from_deps = [d[JavaSdkLibraryInfo].module_lib for d in ctx.attr.deps] + elif ctx.attr.scope == "system-server": + from_deps = [d[JavaSdkLibraryInfo].system_server for d in ctx.attr.deps] + inputs.extend(from_deps) + return depset(inputs) + +def _get_output_name(ctx): + output_name = "current.txt" + if ctx.attr.scope != "public": + output_name = ctx.attr.scope + "-" + output_name + return output_name + +def _merged_txts_impl(ctx): + output = ctx.actions.declare_file(_get_output_name(ctx)) + inputs = _get_inputs(ctx) + args = ctx.actions.args() + args.add_all(METALAVA_ARGS) + args.add_all(inputs) + args.add("--api", output) + ctx.actions.run( + outputs = [output], + inputs = inputs, + executable = ctx.executable._metalava, + arguments = [args], + ) + return [DefaultInfo(files = depset([output]))] + +merged_txts = rule( + implementation = _merged_txts_impl, + attrs = { + "scope": attr.string( + doc = "api scope", + ), + "base": attr.label( + mandatory = True, + allow_single_file = True, + doc = "the target used to get the checked-in base current.txt", + ), + "deps": attr.label_list( + mandatory = True, + allow_empty = False, + providers = [JavaSdkLibraryInfo], + ), + "_metalava": attr.label( + default = "//tools/metalava:metalava", + executable = True, + cfg = "exec", + ), + }, +) diff --git a/rules/java/merged_txts_test.bzl b/rules/java/merged_txts_test.bzl new file mode 100644 index 00000000..6dcf3331 --- /dev/null +++ b/rules/java/merged_txts_test.bzl @@ -0,0 +1,156 @@ +""" +Copyright (C) 2023 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +load("@bazel_skylib//lib:paths.bzl", "paths") +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load(":merged_txts.bzl", "merged_txts") +load(":sdk_library.bzl", "java_sdk_library") + +SCOPE_TO_JAVA_SDK_LIBRARY_FILE = { + "public": "sdk_public.txt", + "system": "sdk_system.txt", + "module-lib": "sdk_module_lib.txt", + "system-server": "sdk_system_server.txt", +} + +def _basic_merged_txts_test_impl(ctx): + env = analysistest.begin(ctx) + actions = analysistest.target_actions(env) + + base_file = paths.join(paths.dirname(ctx.build_file_path), ctx.attr.base) + asserts.true( + env, + base_file in actions[0].argv, + "Base file {} of scope {} is not in args list".format(base_file, ctx.attr.scope), + ) + + java_sdk_library_file = paths.join( + paths.dirname(ctx.build_file_path), + SCOPE_TO_JAVA_SDK_LIBRARY_FILE[ctx.attr.scope], + ) + asserts.true( + env, + java_sdk_library_file in actions[0].argv, + "java_sdk_library file {} of scope {} is not in args list".format(java_sdk_library_file, ctx.attr.scope), + ) + + return analysistest.end(env) + +basic_merged_txts_test = analysistest.make( + _basic_merged_txts_test_impl, + attrs = { + "scope": attr.string(), + "base": attr.string(), + }, +) + +def test_generated_current_txt(): + name = "generated_current_txt_test" + target_name = name + "_target" + scope = "public" + base = "non-updatable-current.txt" + merged_txts( + name = target_name, + scope = scope, + base = base, + deps = ["dep"], + tags = ["manual"], + ) + java_sdk_library( + name = "dep", + public = SCOPE_TO_JAVA_SDK_LIBRARY_FILE["public"], + system = SCOPE_TO_JAVA_SDK_LIBRARY_FILE["system"], + module_lib = SCOPE_TO_JAVA_SDK_LIBRARY_FILE["module-lib"], + system_server = SCOPE_TO_JAVA_SDK_LIBRARY_FILE["system-server"], + ) + basic_merged_txts_test( + name = name, + target_under_test = target_name, + scope = scope, + base = base, + ) + return name + +def test_generated_system_current_txt(): + name = "generated_system_current_txt_test" + target_name = name + "_target" + scope = "system" + base = "non-updatable-system-current.txt" + merged_txts( + name = target_name, + scope = scope, + base = base, + deps = ["dep"], + tags = ["manual"], + ) + basic_merged_txts_test( + name = name, + target_under_test = target_name, + scope = scope, + base = base, + ) + return name + +def test_generated_module_lib_current_txt(): + name = "generated_module_lib_current_txt_test" + target_name = name + "_target" + scope = "module-lib" + base = "non-updatable-module-lib_current.txt" + merged_txts( + name = target_name, + scope = scope, + base = base, + deps = ["dep"], + tags = ["manual"], + ) + basic_merged_txts_test( + name = name, + target_under_test = target_name, + scope = scope, + base = base, + ) + return name + +def test_generated_system_server_current_txt(): + name = "generated_system_server_current_txt_test" + target_name = name + "_target" + scope = "system-server" + base = "non-updatable-system-server-current.txt" + merged_txts( + name = target_name, + scope = scope, + base = base, + deps = ["dep"], + tags = ["manual"], + ) + basic_merged_txts_test( + name = name, + target_under_test = target_name, + scope = scope, + base = base, + ) + return name + +def merged_txts_test_suite(name): + native.test_suite( + name = name, + tests = [ + test_generated_current_txt(), + test_generated_system_current_txt(), + test_generated_module_lib_current_txt(), + test_generated_system_server_current_txt(), + ], + ) diff --git a/rules/java/proto.bzl b/rules/java/proto.bzl index fa2d5675..68533d20 100644 --- a/rules/java/proto.bzl +++ b/rules/java/proto.bzl @@ -1,22 +1,19 @@ -""" -Copyright (C) 2021 The Android Open Source Project - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. load("//build/bazel/rules:proto_file_utils.bzl", "proto_file_utils") -load("@bazel_skylib//lib:paths.bzl", "paths") -load(":library.bzl", "java_library") +load(":rules.bzl", "java_library") def _java_proto_sources_gen_rule_impl(ctx): out_flags = [] @@ -30,17 +27,20 @@ def _java_proto_sources_gen_rule_impl(ctx): out_flags.append(ctx.attr.out_format) srcs = [] + proto_infos = [] + for dep in ctx.attr.deps: - proto_info = dep[ProtoInfo] - out_jar = _generate_java_proto_action( - proto_info = proto_info, - protoc = ctx.executable._protoc, - ctx = ctx, - out_flags = out_flags, - plugin_executable = plugin_executable, - out_arg = out_arg, - ) - srcs.append(out_jar) + proto_infos.append(dep[ProtoInfo]) + + out_jar = _generate_java_proto_action( + proto_infos = proto_infos, + protoc = ctx.executable._protoc, + ctx = ctx, + out_flags = out_flags, + plugin_executable = plugin_executable, + out_arg = out_arg, + ) + srcs.append(out_jar) return [ DefaultInfo(files = depset(direct = srcs)), @@ -76,14 +76,14 @@ If not provided, defaults to full protos. ) def _generate_java_proto_action( - proto_info, + proto_infos, protoc, ctx, plugin_executable, out_arg, out_flags): return proto_file_utils.generate_jar_proto_action( - proto_info, + proto_infos, protoc, ctx, out_flags, @@ -96,9 +96,10 @@ def _java_proto_library( name, deps = [], plugin = None, - target_compatible_with = [], out_format = None, - proto_dep = None): + proto_dep = None, + sdk_version = "core_current", + **kwargs): proto_sources_name = name + "_proto_gen" _java_proto_sources_gen( @@ -106,6 +107,7 @@ def _java_proto_library( deps = deps, plugin = plugin, out_format = out_format, + tags = ["manual"], ) if proto_dep: @@ -117,70 +119,61 @@ def _java_proto_library( name = name, srcs = [proto_sources_name], deps = deps, - target_compatible_with = target_compatible_with, + sdk_version = sdk_version, + **kwargs ) def java_nano_proto_library( name, - deps = [], plugin = "//external/protobuf:protoc-gen-javanano", - target_compatible_with = []): + **kwargs): _java_proto_library( name, - deps = deps, plugin = plugin, - target_compatible_with = target_compatible_with, proto_dep = "//external/protobuf:libprotobuf-java-nano", + **kwargs ) def java_micro_proto_library( name, - deps = [], plugin = "//external/protobuf:protoc-gen-javamicro", - target_compatible_with = []): + **kwargs): _java_proto_library( name, - deps = deps, plugin = plugin, - target_compatible_with = target_compatible_with, proto_dep = "//external/protobuf:libprotobuf-java-micro", + **kwargs ) def java_lite_proto_library( name, - deps = [], plugin = None, - target_compatible_with = []): + **kwargs): _java_proto_library( name, - deps = deps, plugin = plugin, - target_compatible_with = target_compatible_with, out_format = "lite", proto_dep = "//external/protobuf:libprotobuf-java-lite", + **kwargs ) def java_stream_proto_library( name, - deps = [], plugin = "//frameworks/base/tools/streaming_proto:protoc-gen-javastream", - target_compatible_with = []): + **kwargs): _java_proto_library( name, - deps = deps, plugin = plugin, - target_compatible_with = target_compatible_with, + **kwargs ) def java_proto_library( name, - deps = [], plugin = None, - target_compatible_with = []): + **kwargs): _java_proto_library( name, - deps = deps, plugin = plugin, - target_compatible_with = target_compatible_with, proto_dep = "//external/protobuf:libprotobuf-java-full", + **kwargs ) diff --git a/rules/java/rules.bzl b/rules/java/rules.bzl new file mode 100644 index 00000000..575321b2 --- /dev/null +++ b/rules/java/rules.bzl @@ -0,0 +1,30 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:#www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load( + "@rules_java//java:defs.bzl", + _java_binary = "java_binary", +) +load( + "//build/bazel/rules/java:import.bzl", + _java_import = "java_import", +) +load( + "//build/bazel/rules/java:library.bzl", + _java_library = "java_library", +) + +java_binary = _java_binary +java_library = _java_library +java_import = _java_import diff --git a/rules/java/sdk/BUILD.bazel b/rules/java/sdk/BUILD.bazel new file mode 100644 index 00000000..b00ba819 --- /dev/null +++ b/rules/java/sdk/BUILD.bazel @@ -0,0 +1,112 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +package( + default_visibility = ["//visibility:public"], +) + +load("@bazel_skylib//rules:common_settings.bzl", "int_setting", "string_setting") +load("//build/bazel/rules/common:sdk_version.bzl", "sdk_version") +load("//build/bazel/rules/common:api.bzl", "api") +load("//build/bazel/rules/java:versions.bzl", "java_versions") +load(":config_setting_names.bzl", "config_setting_names") +load("@bazel_skylib//lib:selects.bzl", "selects") +load("//prebuilts/sdk:utils.bzl", "prebuilt_sdk_utils") + +string_setting( + name = "kind", + build_setting_default = sdk_version.KIND_PUBLIC, + values = sdk_version.ALL_KINDS, +) + +int_setting( + name = "api_level", + build_setting_default = api.FUTURE_API_LEVEL, +) + +# The settings below are used to properly define a device java and android toolchain. + +# The SDK_NONE config setting maps to sdk_version = "none". In this configuration the java toolchain +# will provide nothing on the bootclasspath, not even the standard java.* libraries. +# The android toolchain is undefined in this configuration. +config_setting( + name = config_setting_names.SDK_NONE, + flag_values = { + "//build/bazel/rules/java/sdk:kind": sdk_version.KIND_NONE, + }, +) + +# Pre and Post Java 9 configs differ in how the bootclasspath is constructed and what arguments must +# be passed to javac. Pre Java 9, the SDK is passed as a whole to the --bootclasspath argument of +# javac. Post Java 9, the SDK is split between core libraries, passed using system modules and the +# --system javac argument, and the rest, added at the beginning of the classpath. +selects.config_setting_group( + name = config_setting_names.PRE_JAVA_9, + match_any = [ + "//build/bazel/rules/java:" + java_versions.VERSION_TO_CONFIG_SETTING[version] + for version in java_versions.VERSION_TO_CONFIG_SETTING.keys() + if version < 9 + ], +) + +selects.config_setting_group( + name = config_setting_names.POST_JAVA_9, + match_any = [ + "//build/bazel/rules/java:" + java_versions.VERSION_TO_CONFIG_SETTING[version] + for version in java_versions.VERSION_TO_CONFIG_SETTING.keys() + if version >= 9 + ], +) + +# Specific configuration at a given kind and api level will have the java and android toolchains +# pointed to the proper android.jar and framework.aidl files. +[ + config_setting( + name = config_setting_names.kind_api(kind, api_level), + flag_values = { + "//build/bazel/rules/java/sdk:kind": kind, + "//build/bazel/rules/java/sdk:api_level": str(api_level), + }, + ) + for api_level in prebuilt_sdk_utils.API_LEVELS + for kind in prebuilt_sdk_utils.available_kinds_for_api_level(api_level) +] + +# The bootclasspath is a function of sdk kind, api level, and whether building for pre or post java +# 9 sources. +[ + selects.config_setting_group( + name = config_setting_names.kind_api_pre_java_9(kind, api_level), + match_all = [ + config_setting_names.kind_api(kind, api_level), + config_setting_names.PRE_JAVA_9, + ], + ) + for api_level in prebuilt_sdk_utils.API_LEVELS + for kind in prebuilt_sdk_utils.available_kinds_for_api_level(api_level) + if java_versions.supports_pre_java_9(api_level) +] + +[ + selects.config_setting_group( + name = config_setting_names.kind_api_post_java_9(kind, api_level), + match_all = [ + config_setting_names.kind_api(kind, api_level), + config_setting_names.POST_JAVA_9, + ], + ) + for api_level in prebuilt_sdk_utils.API_LEVELS + for kind in prebuilt_sdk_utils.available_kinds_for_api_level(api_level) + if java_versions.supports_post_java_9(api_level) +] diff --git a/rules/java/sdk/config_setting_names.bzl b/rules/java/sdk/config_setting_names.bzl new file mode 100644 index 00000000..2150465f --- /dev/null +++ b/rules/java/sdk/config_setting_names.bzl @@ -0,0 +1,35 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +def _kind_api(kind, api_level): + return "config_setting_android_%s_%s" % (kind, api_level) + +def _kind_api_pre_java_9(kind, api_level): + return _kind_api(kind, api_level) + "_pre_java_9" + +def _kind_api_post_java_9(kind, api_level): + return _kind_api(kind, api_level) + "_post_java_9" + +_CONFIG_SETTING_SDK_NONE = "config_setting_sdk_none" +_CONFIG_SETTING_PRE_JAVA_9 = "config_setting_pre_java_9" +_CONFIG_SETTING_POST_JAVA_9 = "config_setting_post_java_9" + +config_setting_names = struct( + SDK_NONE = _CONFIG_SETTING_SDK_NONE, + PRE_JAVA_9 = _CONFIG_SETTING_PRE_JAVA_9, + POST_JAVA_9 = _CONFIG_SETTING_POST_JAVA_9, + kind_api = _kind_api, + kind_api_pre_java_9 = _kind_api_pre_java_9, + kind_api_post_java_9 = _kind_api_post_java_9, +) diff --git a/rules/java/sdk_library.bzl b/rules/java/sdk_library.bzl new file mode 100644 index 00000000..82f6eb3c --- /dev/null +++ b/rules/java/sdk_library.bzl @@ -0,0 +1,61 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +JavaSdkLibraryInfo = provider( + "Checked in current.txt for Public, System, Module_lib and System_server", + fields = [ + "public", + "system", + "test", + "module_lib", + "system_server", + ], +) + +def _java_sdk_library_impl(ctx): + return [ + JavaSdkLibraryInfo( + public = ctx.file.public, + system = ctx.file.system, + test = ctx.file.test, + module_lib = ctx.file.module_lib, + system_server = ctx.file.system_server, + ), + ] + +java_sdk_library = rule( + implementation = _java_sdk_library_impl, + attrs = { + "public": attr.label( + allow_single_file = True, + doc = "public api surface file", + ), + "system": attr.label( + allow_single_file = True, + doc = "system api surface file", + ), + "test": attr.label( + allow_single_file = True, + doc = "test api surface file", + ), + "module_lib": attr.label( + allow_single_file = True, + doc = "module_lib api surface file", + ), + "system_server": attr.label( + allow_single_file = True, + doc = "system_server api surface file", + ), + }, +) diff --git a/rules/java/sdk_library_test.bzl b/rules/java/sdk_library_test.bzl new file mode 100644 index 00000000..ca954766 --- /dev/null +++ b/rules/java/sdk_library_test.bzl @@ -0,0 +1,115 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load(":sdk_library.bzl", "JavaSdkLibraryInfo", "java_sdk_library") + +def _basic_java_sdk_library_test_impl(ctx): + env = analysistest.begin(ctx) + java_sdk_library_target = analysistest.target_under_test(env) + + asserts.true( + env, + java_sdk_library_target[JavaSdkLibraryInfo].public.is_source, + "Public api surface file should be source, not generated", + ) + + asserts.equals( + env, + expected = "public.txt", + actual = java_sdk_library_target[JavaSdkLibraryInfo].public.basename, + msg = "Public api surface file not correct", + ) + + asserts.true( + env, + java_sdk_library_target[JavaSdkLibraryInfo].system.is_source, + "System api surface file should be source, not generated", + ) + + asserts.equals( + env, + expected = "system.txt", + actual = java_sdk_library_target[JavaSdkLibraryInfo].system.basename, + msg = "System api surface file not correct", + ) + + asserts.true( + env, + java_sdk_library_target[JavaSdkLibraryInfo].test.is_source, + "Test api surface file should be source, not generated", + ) + + asserts.equals( + env, + expected = "test.txt", + actual = java_sdk_library_target[JavaSdkLibraryInfo].test.basename, + msg = "Test api surface file not correct", + ) + + asserts.true( + env, + java_sdk_library_target[JavaSdkLibraryInfo].module_lib.is_source, + "Module_lib api surface file should be source, not generated", + ) + + asserts.equals( + env, + expected = "module_lib.txt", + actual = java_sdk_library_target[JavaSdkLibraryInfo].module_lib.basename, + msg = "Module_lib api surface file not correct", + ) + + asserts.true( + env, + java_sdk_library_target[JavaSdkLibraryInfo].system_server.is_source, + "System_server api surface file should be source, not generated", + ) + + asserts.equals( + env, + expected = "system_server.txt", + actual = java_sdk_library_target[JavaSdkLibraryInfo].system_server.basename, + msg = "System_server api surface file not correct", + ) + + return analysistest.end(env) + +basic_java_sdk_library_test = analysistest.make( + _basic_java_sdk_library_test_impl, +) + +def test_checked_in_api_surface_files(): + name = "checked_in_api_surface_files_test" + java_sdk_library( + name = name + "_target", + public = "public.txt", + system = "system.txt", + test = "test.txt", + module_lib = "module_lib.txt", + system_server = "system_server.txt", + ) + basic_java_sdk_library_test( + name = name, + target_under_test = name + "_target", + ) + return name + +def java_sdk_library_test_suite(name): + native.test_suite( + name = name, + tests = [ + test_checked_in_api_surface_files(), + ], + ) diff --git a/rules/java/sdk_transition.bzl b/rules/java/sdk_transition.bzl new file mode 100644 index 00000000..7f69d09d --- /dev/null +++ b/rules/java/sdk_transition.bzl @@ -0,0 +1,68 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("//build/bazel/rules/java:versions.bzl", "java_versions") +load("//build/bazel/rules/common:sdk_version.bzl", "sdk_version") +load("//build/bazel/rules/common:api.bzl", "api") + +def _sdk_transition_impl(settings, attr): + host_platform = settings["//command_line_option:host_platform"] + default_java_version = str(java_versions.get_version()) + + # TODO: this condition should really be "platform is not a device". + # More details on why we're treating java version for non-device platforms differently at the + # definition of the //build/bazel/rules/java:host_version build setting. + if all([host_platform == platform for platform in settings["//command_line_option:platforms"]]): + return { + "//build/bazel/rules/java:version": default_java_version, + "//build/bazel/rules/java:host_version": str(java_versions.get_version(attr.java_version)), + "//build/bazel/rules/java/sdk:kind": sdk_version.KIND_NONE, + "//build/bazel/rules/java/sdk:api_level": api.NONE_API_LEVEL, + } + sdk_spec = sdk_version.sdk_spec_from(attr.sdk_version) + java_version = str(java_versions.get_version(attr.java_version, sdk_spec.api_level)) + + return { + "//build/bazel/rules/java:host_version": default_java_version, + "//build/bazel/rules/java:version": java_version, + "//build/bazel/rules/java/sdk:kind": sdk_spec.kind, + "//build/bazel/rules/java/sdk:api_level": sdk_spec.api_level, + } + +sdk_transition = transition( + implementation = _sdk_transition_impl, + inputs = [ + "//command_line_option:host_platform", + "//command_line_option:platforms", + ], + outputs = [ + "//build/bazel/rules/java:version", + "//build/bazel/rules/java:host_version", + "//build/bazel/rules/java/sdk:kind", + "//build/bazel/rules/java/sdk:api_level", + ], +) + +sdk_transition_attrs = { + # This attribute must have a specific name to let the DexArchiveAspect propagate + # through it. + "exports": attr.label( + cfg = sdk_transition, + ), + "java_version": attr.string(), + "sdk_version": attr.string(), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), +} diff --git a/rules/java/sdk_transition_test.bzl b/rules/java/sdk_transition_test.bzl new file mode 100644 index 00000000..6dd90441 --- /dev/null +++ b/rules/java/sdk_transition_test.bzl @@ -0,0 +1,173 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load(":sdk_transition.bzl", "sdk_transition") +load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") +load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo") + +SdkConfig = provider( + "Info about the config settings of the leaf dependency (in a linear dependency chain only)", + fields = { + "java_version": "the value of the //build/bazel/rules/java:version setting.", + "host_java_version": "the value of the //build/bazel/rules/java:host_version setting.", + "sdk_kind": "the value of the //build/bazel/rules/java/sdk:kind setting.", + "api_level": "the value of the //build/bazel/rules/java/sdk:api_level setting.", + }, +) + +def _sdk_transition_tester_impl(ctx): + if ctx.attr.exports and len(ctx.attr.exports) > 0 and SdkConfig in ctx.attr.exports[0]: + return ctx.attr.exports[0][SdkConfig] + return SdkConfig( + java_version = ctx.attr._java_version_config_setting[BuildSettingInfo].value, + host_java_version = ctx.attr._host_java_version_config_setting[BuildSettingInfo].value, + sdk_kind = ctx.attr._sdk_kind_config_setting[BuildSettingInfo].value, + api_level = ctx.attr._api_level_config_setting[BuildSettingInfo].value, + ) + +sdk_transition_tester = rule( + implementation = _sdk_transition_tester_impl, + attrs = { + "exports": attr.label( + cfg = sdk_transition, + providers = [SdkConfig], + ), + "java_version": attr.string(), + "sdk_version": attr.string(), + "_java_version_config_setting": attr.label( + default = "//build/bazel/rules/java:version", + ), + "_host_java_version_config_setting": attr.label( + default = "//build/bazel/rules/java:host_version", + ), + "_sdk_kind_config_setting": attr.label( + default = "//build/bazel/rules/java/sdk:kind", + ), + "_api_level_config_setting": attr.label( + default = "//build/bazel/rules/java/sdk:api_level", + ), + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + }, +) + +def _sdk_transition_host_test_impl(ctx): + env = analysistest.begin(ctx) + actual_config = analysistest.target_under_test(env)[SdkConfig] + asserts.equals( + env, + ctx.attr.expected_host_java_version, + actual_config.host_java_version, + "mismatching host_java_version", + ) + return analysistest.end(env) + +def _sdk_transition_device_test_impl(ctx): + env = analysistest.begin(ctx) + actual_config = analysistest.target_under_test(env)[SdkConfig] + asserts.equals( + env, + ctx.attr.expected_java_version, + actual_config.java_version, + "mismatching java_version", + ) + asserts.equals( + env, + ctx.attr.expected_sdk_kind, + actual_config.sdk_kind, + "mismatching sdk_kind", + ) + asserts.equals( + env, + ctx.attr.expected_api_level, + actual_config.api_level, + "mismatching api_level", + ) + return analysistest.end(env) + +sdk_transition_host_test = analysistest.make( + impl = _sdk_transition_host_test_impl, + attrs = { + "expected_host_java_version": attr.string(), + }, + config_settings = { + "//command_line_option:platforms": "@//build/bazel/tests/products:aosp_arm64_for_testing_linux_x86_64", + "//command_line_option:host_platform": "@//build/bazel/tests/products:aosp_arm64_for_testing_linux_x86_64", + }, +) + +sdk_transition_device_test = analysistest.make( + impl = _sdk_transition_device_test_impl, + attrs = { + "expected_java_version": attr.string(), + "expected_sdk_kind": attr.string(), + "expected_api_level": attr.int(), + }, + config_settings = { + "//command_line_option:platforms": "@//build/bazel/tests/products:aosp_arm64_for_testing", + "//command_line_option:host_platform": "@//build/bazel/tests/products:aosp_arm64_for_testing_linux_x86_64", + }, +) + +def set_up_targets_under_test(name, java_version, sdk_version): + sdk_transition_tester( + name = name + "_parent", + java_version = java_version, + sdk_version = sdk_version, + exports = name + "_child", + tags = ["manual"], + ) + sdk_transition_tester( + name = name + "_child", + tags = ["manual"], + ) + +def test_host_sdk_transition( + name, + java_version, + expected_host_java_version): + set_up_targets_under_test(name, java_version, sdk_version = None) + sdk_transition_host_test( + name = name, + target_under_test = name + "_parent", + expected_host_java_version = expected_host_java_version, + ) + return name + +def test_device_sdk_transition( + name, + java_version, + sdk_version, + expected_java_version, + expected_sdk_kind, + expected_api_level): + set_up_targets_under_test(name, java_version, sdk_version) + sdk_transition_device_test( + name = name, + target_under_test = name + "_parent", + expected_java_version = expected_java_version, + expected_sdk_kind = expected_sdk_kind, + expected_api_level = expected_api_level, + ) + return name + +def sdk_transition_test_suite(name): + native.test_suite( + name = name, + tests = [ + test_host_sdk_transition("test_host_sdk_transition", java_version = "8", expected_host_java_version = "8"), + test_device_sdk_transition("test_device_sdk_transition", java_version = "9", sdk_version = "32", expected_java_version = "9", expected_sdk_kind = "public", expected_api_level = 32), + ], + ) diff --git a/rules/java/stub_local_jdk/BUILD.bazel b/rules/java/stub_local_jdk/BUILD.bazel new file mode 100644 index 00000000..0902d775 --- /dev/null +++ b/rules/java/stub_local_jdk/BUILD.bazel @@ -0,0 +1,8 @@ +package(default_visibility = ["//visibility:public"]) + +#The Kotlin rules directly refer to @local_jdk//jar, this alias is defined to +# keep this reference valid. +alias( + name = "jar", + actual = "@//prebuilts/jdk/jdk17:jar", +) diff --git a/rules/java/stub_local_jdk/WORKSPACE b/rules/java/stub_local_jdk/WORKSPACE new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/rules/java/stub_local_jdk/WORKSPACE diff --git a/rules/java/versions.bzl b/rules/java/versions.bzl new file mode 100644 index 00000000..acee980e --- /dev/null +++ b/rules/java/versions.bzl @@ -0,0 +1,104 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Constants and utility functions relating to Java versions and how they map to SDK versions. +""" + +load("//build/bazel/rules/common:api.bzl", "api") + +# The default java version used absent any java_version or sdk_version specification. +_DEFAULT_VERSION = 17 + +# All available java versions +_ALL_VERSIONS = [ + 7, + 8, + 9, + 11, + 17, +] + +_VERSION_TO_CONFIG_SETTING = { + java_version: "config_setting_java_%s" % java_version + for java_version in _ALL_VERSIONS +} + +def _compatible_versions_for_api_level(api_level): + """Returns all possible java versions that can be used at the given api level.""" + if api_level in (api.FUTURE_API_LEVEL, api.NONE_API_LEVEL): + return _ALL_VERSIONS + if api_level <= 23: + return [7] + if api_level <= 29: + return [ + 7, + 8, + ] + if api_level <= 31: + return [ + 7, + 8, + 9, + ] + if api_level <= 33: + return [ + 7, + 8, + 9, + 11, + ] + return _ALL_VERSIONS + +def _supports_pre_java_9(api_level): + return any([ + version < 9 + for version in _compatible_versions_for_api_level(api_level) + ]) + +def _supports_post_java_9(api_level): + return any([ + version >= 9 + for version in _compatible_versions_for_api_level(api_level) + ]) + +_NORMALIZED_VERSIONS = { + "1.7": 7, + "7": 7, + "1.8": 8, + "8": 8, + "1.9": 9, + "9": 9, + "11": 11, + "17": 17, +} + +def _default_version(api_level): + """Returns the default java version for the input api level.""" + return max(_compatible_versions_for_api_level(api_level)) + +def _get_version(java_version = None, api_level = None): + """Returns the java version to use for a given target based on the java_version set by this target and the api_level_string extracted from sdk_version.""" + if java_version: + return _NORMALIZED_VERSIONS[java_version] + elif api_level: + return _default_version(api_level) + return _DEFAULT_VERSION + +java_versions = struct( + ALL_VERSIONS = _ALL_VERSIONS, + VERSION_TO_CONFIG_SETTING = _VERSION_TO_CONFIG_SETTING, + compatible_versions_for_api_level = _compatible_versions_for_api_level, + get_version = _get_version, + supports_pre_java_9 = _supports_pre_java_9, + supports_post_java_9 = _supports_post_java_9, +) diff --git a/rules/java/versions_test.bzl b/rules/java/versions_test.bzl new file mode 100644 index 00000000..f06488b1 --- /dev/null +++ b/rules/java/versions_test.bzl @@ -0,0 +1,44 @@ +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") +load("//build/bazel/rules/java:versions.bzl", "java_versions") +load("//build/bazel/rules/common:api.bzl", "api") + +def _get_java_version_test_impl(ctx): + env = unittest.begin(ctx) + + _VERSIONS_UNDER_TEST = { + (None, api.FUTURE_API_LEVEL): 17, + (None, 23): 7, + (None, 33): 11, + ("1.7", api.FUTURE_API_LEVEL): 7, + ("1.7", 23): 7, + ("1.8", 33): 8, + (None, None): 17, + } + for java_sdk_version, expected_java_version in _VERSIONS_UNDER_TEST.items(): + java_version = java_sdk_version[0] + sdk_version = java_sdk_version[1] + asserts.equals(env, expected_java_version, java_versions.get_version(java_version, sdk_version), "unexpected java version for java_version %s and sdk_version %s" % (java_version, sdk_version)) + + return unittest.end(env) + +get_java_version_test = unittest.make(_get_java_version_test_impl) + +def versions_test_suite(name): + unittest.suite( + name, + get_java_version_test, + ) diff --git a/rules/java/wrapper_test.sh b/rules/java/wrapper_test.sh new file mode 100644 index 00000000..abab4214 --- /dev/null +++ b/rules/java/wrapper_test.sh @@ -0,0 +1,38 @@ +#!/bin/bash -eux + +# Copyright (C) 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [[ ! -d "build/bazel/ci" ]]; then + echo "Please run this script from TOP". + exit 1 +fi + +source "build/bazel/ci/build_with_bazel.sh" +source "build/bazel/ci/target_lists.sh" + +function test_wrapper_providers() { + for target in ${EXAMPLE_WRAPPER_TARGETS[@]}; do + private_providers="$(build/bazel/bin/bazel ${STARTUP_FLAGS[@]} \ + cquery ${FLAGS[@]} --config=android "${target}_private" \ + --starlark:expr="sorted(providers(target).keys())" --output=starlark|uniq)" + wrapper_providers="$(build/bazel/bin/bazel ${STARTUP_FLAGS[@]} \ + cquery ${FLAGS[@]} --config=android "${target}" \ + --starlark:expr="sorted(providers(target).keys())" --output=starlark|uniq)" + if [[ ! $(cmp -s <(echo "${private_providers}") <(echo "${wrapper_providers}")) ]]; then + echo "${target} and ${target}_private should have the same providers. Diff:" + diff <(echo "${private_providers}") <(echo "${wrapper_providers}") + fi + done +} |