From de45065d2b95a48e6d7171e80f3282601aac00f3 Mon Sep 17 00:00:00 2001 From: ilist Date: Thu, 22 Jul 2021 04:30:10 -0700 Subject: [PATCH] Restructure Starlark java_library to internal and Bazel parts. PiperOrigin-RevId: 386209364 --- .../builtins_bzl/common/java/android_lint.bzl | 120 ++++++++++++++ .../common/java/compile_action.bzl | 156 ++++++++++++++++++ .../common/java/java_semantics.bzl | 37 +++++ .../common/java/proguard_validation.bzl | 77 +++++++++ .../builtins_bzl/common/rule_util.bzl | 88 ++++++++++ 5 files changed, 478 insertions(+) create mode 100644 src/main/starlark/builtins_bzl/common/java/android_lint.bzl create mode 100644 src/main/starlark/builtins_bzl/common/java/compile_action.bzl create mode 100644 src/main/starlark/builtins_bzl/common/java/java_semantics.bzl create mode 100644 src/main/starlark/builtins_bzl/common/java/proguard_validation.bzl create mode 100644 src/main/starlark/builtins_bzl/common/rule_util.bzl diff --git a/src/main/starlark/builtins_bzl/common/java/android_lint.bzl b/src/main/starlark/builtins_bzl/common/java/android_lint.bzl new file mode 100644 index 00000000000000..e48707f0cce06a --- /dev/null +++ b/src/main/starlark/builtins_bzl/common/java/android_lint.bzl @@ -0,0 +1,120 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# 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. + +"""Creates the android lint action for java rules""" + +load(":common/rule_util.bzl", "create_dep") + +java_common = _builtins.toplevel.java_common + +def _impl(ctx, java_info, source_files, source_jars): + # assuming that linting is enabled for all java rules i.e. + # --experimental_run_android_lint_on_java_rules=true and + # --experimental_limit_android_lint_to_android_constrained_java=false + if (ctx.configuration == ctx.host_configuration or + ctx.bin_dir.path.find("-exec-") >= 0): + return None + + srcs = ctx.files.srcs + if not srcs or (hasattr(ctx.attr, "neverlink") and ctx.attr.neverlink): + return None + + toolchain = ctx.attr._java_toolchain[java_common.JavaToolchainInfo] + java_runtime = toolchain.java_runtime + linter = toolchain.android_linter() + if not linter: + # TODO(hvd): enable after enabling in tests + # fail("android linter not set in java_toolchain") + return None + + args = ctx.actions.args() + + tool = linter.tool + executable = linter.tool.executable + transitive_inputs = [linter.data] + if executable.extension == "jar": + args.add_all(toolchain.jvm_opt) + args.add("-jar", executable) + executable = java_runtime.java_executable_exec_path + transitive_inputs.append(java_runtime.files) + + for output in java_info.java_outputs: + if output.generated_source_jar != None: + source_jars.append(output.generated_source_jar) + + # TODO(ilist): collect compile_jars from JavaInfo in deps & exports + classpath = java_info.compilation_info.compilation_classpath + + # TODO(hvd): get from toolchain if we need this - probably android only + bootclasspath_aux = [] + if bootclasspath_aux: + classpath = depset(transitive = [classpath, bootclasspath_aux]) + + bootclasspath = toolchain.bootclasspath + plugin_info = java_info.plugins + + args.add_all("--sources", source_files) + args.add_all("--source_jars", source_jars) + args.add_all("--bootclasspath", bootclasspath) + args.add_all("--classpath", classpath) + args.add_all("--plugins", plugin_info.processor_jars) + args.add("--target_label", ctx.label) + + javac_opts = java_info.compilation_info.javac_options + if (javac_opts): + args.add_all("--javacopts", javac_opts) + args.add("--") + + args.add("--lintopts") + args.add_all(linter.lint_opts) + + for package_config in linter.package_config: + if package_config.matches(ctx.label): + args.add_all(package_config.javac_opts()) + transitive_inputs.append(package_config.data()) + + android_lint_out = ctx.actions.declare_file("%s_android_lint_output.xml" % ctx.label.name) + args.add("--xml", android_lint_out) + + args.set_param_file_format(format = "multiline") + args.use_param_file(param_file_arg = "@%s", use_always = True) + ctx.actions.run( + mnemonic = "AndroidLint", + progress_message = "Running Android Lint for: %{label}", + executable = executable, + inputs = depset( + source_files + source_jars, + transitive = transitive_inputs + [ + classpath, + bootclasspath, + plugin_info.processor_jars, + plugin_info.processor_data, + ], + ), + outputs = [android_lint_out], + tools = [tool], + arguments = [args], + execution_requirements = {"supports-workers": "1"}, + ) + return android_lint_out + +ANDROID_LINT_ACTION = create_dep( + call = _impl, + attrs = { + "_java_toolchain": attr.label( + default = "@//tools/jdk:current_java_toolchain", + providers = [java_common.JavaToolchainInfo], + ), + }, +) diff --git a/src/main/starlark/builtins_bzl/common/java/compile_action.bzl b/src/main/starlark/builtins_bzl/common/java/compile_action.bzl new file mode 100644 index 00000000000000..d6d697052ddfef --- /dev/null +++ b/src/main/starlark/builtins_bzl/common/java/compile_action.bzl @@ -0,0 +1,156 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# 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. + +""" +Java compile action +""" + +load(":common/rule_util.bzl", "create_dep") +load( + ":common/java/java_semantics.bzl", + "ALLOWED_RULES_IN_DEPS", + "ALLOWED_RULES_IN_DEPS_WITH_WARNING", + "COLLECT_SRCS_FROM_PROTO_LIBRARY", + "EXPERIMENTAL_USE_FILEGROUPS_IN_JAVALIBRARY", + "EXTRA_SRCS_TYPES", +) + +java_common = _builtins.toplevel.java_common +ProtoInfo = _builtins.toplevel.ProtoInfo +DefaultInfo = _builtins.toplevel.DefaultInfo +CcInfo = _builtins.toplevel.CcInfo +JavaInfo = _builtins.toplevel.JavaInfo +JavaPluginInfo = _builtins.toplevel.JavaPluginInfo + +def _get_attr_safe(ctx, attr, default): + return getattr(ctx.attr, attr) if hasattr(ctx.attr, attr) else default + +def _filter_srcs(srcs, ext): + return [f for f in srcs if f.extension == ext] + +def _filter_provider(provider, *attrs): + return [dep[provider] for attr in attrs for dep in attr if provider in dep] + +def _filter_strict_deps(mode): + return "error" if mode in ["strict", "default"] else mode + +# TODO(b/11285003): disallow jar files in deps, require java_import instead +def _legacy_jars(attr): + jars = [ + file + for dep in attr + if not JavaInfo in dep or dep.kind == "java_binary" + for file in dep[DefaultInfo].files.to_list() + if file.extension == "jar" + ] + return [JavaInfo(output_jar = jar, compile_jar = jar) for jar in jars] # Native doesn't construct JavaInfo + +def _compile_action(ctx, extra_resources, source_files, source_jars, output_prefix): + deps = ctx.attr.deps + runtime_deps = _get_attr_safe(ctx, "runtime_deps", []) + exports = _get_attr_safe(ctx, "exports", []) + exported_plugins = _get_attr_safe(ctx, "exported_plugins", []) + + srcs = ctx.files.srcs + + resources = [] + if COLLECT_SRCS_FROM_PROTO_LIBRARY: + for resource in ctx.attr.resources: + if ProtoInfo in resource: + resources.extend(resource[ProtoInfo].transitive_sources.to_list()) + else: + resources.extend(resource[DefaultInfo].files.to_list()) + else: + resources.extend(ctx.files.resources) + + resources.extend(_filter_srcs(ctx.files.srcs, "properties")) + resources.extend(extra_resources) + + java_info = java_common.compile( + ctx, + source_files = source_files, + source_jars = source_jars, + resources = resources, + plugins = _filter_provider(JavaPluginInfo, ctx.attr.plugins) + [ctx.attr._java_plugins[JavaPluginInfo]], + deps = _filter_provider(JavaInfo, deps) + _legacy_jars(deps), + native_libraries = _filter_provider(CcInfo, deps, runtime_deps, exports), + runtime_deps = _filter_provider(JavaInfo, runtime_deps) + _legacy_jars(runtime_deps), + exports = _filter_provider(JavaInfo, exports) + _legacy_jars(exports), + exported_plugins = _filter_provider(JavaPluginInfo, exported_plugins), + javac_opts = [ctx.expand_location(opt) for opt in ctx.attr.javacopts], + neverlink = ctx.attr.neverlink, + java_toolchain = ctx.attr._java_toolchain[java_common.JavaToolchainInfo], + output = ctx.actions.declare_file(output_prefix + "%s.jar" % ctx.attr.name) if EXPERIMENTAL_USE_FILEGROUPS_IN_JAVALIBRARY else ctx.outputs.classjar, + output_source_jar = ctx.actions.declare_file(output_prefix + "%s-src.jar" % ctx.attr.name) if EXPERIMENTAL_USE_FILEGROUPS_IN_JAVALIBRARY else ctx.outputs.sourcejar, + strict_deps = _filter_strict_deps(ctx.fragments.java.strict_java_deps), + ) + + files = [out.class_jar for out in java_info.java_outputs] + + if ctx.attr.neverlink: + runfiles = None + else: + has_sources = source_files or source_jars + run_files = files if has_sources or resources else [] + runfiles = ctx.runfiles(files = run_files, collect_default = True) + runfiles = runfiles.merge_all([dep[DefaultInfo].default_runfiles for attr in [runtime_deps, exports] for dep in attr]) + + default_info = DefaultInfo( + files = depset(files), + runfiles = runfiles, + ) + return java_info, default_info + +COMPILE_ACTION = create_dep( + _compile_action, + { + "srcs": attr.label_list( + allow_files = [".java", ".srcjar", ".properties"] + EXTRA_SRCS_TYPES, + flags = ["DIRECT_COMPILE_TIME_INPUT", "ORDER_INDEPENDENT"], + ), + "data": attr.label_list( + allow_files = True, + flags = ["SKIP_CONSTRAINTS_OVERRIDE"], + ), + "resources": attr.label_list( + allow_files = True, + flags = ["SKIP_CONSTRAINTS_OVERRIDE", "ORDER_INDEPENDENT"], + ), + "plugins": attr.label_list( + providers = [JavaPluginInfo], + allow_files = True, + cfg = "exec", + ), + "deps": attr.label_list( + allow_files = [".jar"], + allow_rules = ALLOWED_RULES_IN_DEPS + ALLOWED_RULES_IN_DEPS_WITH_WARNING, + providers = [ + [CcInfo], + [JavaInfo], + ], + flags = ["SKIP_ANALYSIS_TIME_FILETYPE_CHECK"], + ), + "javacopts": attr.string_list(), + "neverlink": attr.bool(), + "_java_toolchain": attr.label( + default = "@//tools/jdk:current_java_toolchain", + providers = [java_common.JavaToolchainInfo], + ), + "_java_plugins": attr.label( + default = "@//tools/jdk:java_plugins_flag_alias", + providers = [JavaPluginInfo], + ), + }, + ["java", "google_java", "cpp"], +) diff --git a/src/main/starlark/builtins_bzl/common/java/java_semantics.bzl b/src/main/starlark/builtins_bzl/common/java/java_semantics.bzl new file mode 100644 index 00000000000000..756e197ea89618 --- /dev/null +++ b/src/main/starlark/builtins_bzl/common/java/java_semantics.bzl @@ -0,0 +1,37 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# 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. + +""" +Java Semantics +""" + +EXPERIMENTAL_USE_FILEGROUPS_IN_JAVALIBRARY = True +COLLECT_SRCS_FROM_PROTO_LIBRARY = False +EXTRA_SRCS_TYPES = [] + +ALLOWED_RULES_IN_DEPS = [ + "cc_binary", # NB: linkshared=1 + "cc_library", + "genrule", + "genproto", # TODO(bazel-team): we should filter using providers instead (starlark rule). + "java_import", + "java_library", + "java_proto_library", + "java_lite_proto_library", + "proto_library", + "sh_binary", + "sh_library", +] + +ALLOWED_RULES_IN_DEPS_WITH_WARNING = [] diff --git a/src/main/starlark/builtins_bzl/common/java/proguard_validation.bzl b/src/main/starlark/builtins_bzl/common/java/proguard_validation.bzl new file mode 100644 index 00000000000000..4b840f2ce14adf --- /dev/null +++ b/src/main/starlark/builtins_bzl/common/java/proguard_validation.bzl @@ -0,0 +1,77 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# 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. + +""" +Proguard +""" + +load(":common/rule_util.bzl", "create_dep") + +ProguardSpecProvider = _builtins.toplevel.ProguardSpecProvider + +def _get_attr_safe(ctx, attr, default): + return getattr(ctx.attr, attr) if hasattr(ctx.attr, attr) else default + +def _filter_provider(provider, *attrs): + return [dep[provider] for attr in attrs for dep in attr if provider in dep] + +def _collect_transitive_proguard_specs(ctx): + attrs = [ + ctx.attr.deps, + _get_attr_safe(ctx, "runtime_deps", []), + _get_attr_safe(ctx, "exports", []), + _get_attr_safe(ctx, "plugins", []), + _get_attr_safe(ctx, "exported_plugins", []), + ] + + return [proguard.specs for proguard in _filter_provider(ProguardSpecProvider, *attrs)] + +def _validate_spec(ctx, spec_file): + validated_proguard_spec = ctx.actions.declare_file( + "validated_proguard/%s/%s_valid" % (ctx.label.name, spec_file.path), + ) + + args = ctx.actions.args() + args.add("--path", spec_file) + args.add("--output", validated_proguard_spec) + + ctx.actions.run( + mnemonic = "ValidateProguard", + progress_message = "Validating proguard configuration %s" % spec_file, + executable = ctx.executable._proguard_allowlister, + arguments = [args], + inputs = [spec_file], + outputs = [validated_proguard_spec], + ) + + return validated_proguard_spec + +def _validate_proguard_specs_impl(ctx): + if ctx.files.proguard_specs and not hasattr(ctx.attr, "_proguard_allowlister"): + fail("_proguard_allowlister is required to use proguard_specs") + + return ProguardSpecProvider( + depset( + [_validate_spec(ctx, spec_file) for spec_file in ctx.files.proguard_specs], + transitive = _collect_transitive_proguard_specs(ctx), + ), + ) + +VALIDATE_PROGUARD_SPECS = create_dep( + _validate_proguard_specs_impl, + { + "proguard_specs": attr.label_list(allow_files = True), + }, + [], +) diff --git a/src/main/starlark/builtins_bzl/common/rule_util.bzl b/src/main/starlark/builtins_bzl/common/rule_util.bzl new file mode 100644 index 00000000000000..1ad9e62fd51688 --- /dev/null +++ b/src/main/starlark/builtins_bzl/common/rule_util.bzl @@ -0,0 +1,88 @@ +# Copyright 2021 The Bazel Authors. All rights reserved. +# +# 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. + +"""Defines create_rule and create_dep macros""" + +def create_rule(impl, attrs = {}, deps = [], fragments = [], **kwargs): + """Creates a rule composed from dependencies. + + Args: + impl: The implementation function of the rule, taking as parameters the + rule ctx followed by the executable function of each dependency + attrs: Dict of attributes required by the rule. These will override any + conflicting attributes specified by dependencies + deps: Dict of name->dependency mappings, with each dependency struct + created using 'create_dep'. The keys of this dict are the parameter + names received by 'impl' + fragments: List of configuration fragments required by the rule + **kwargs: extra args to be passed for rule creation + + Returns: + The composed rule + """ + merged_attrs = dict() + fragments = list(fragments) + for dep in deps: + merged_attrs.update(dep.attrs) + fragments.extend(dep.fragments) + merged_attrs.update(attrs) + + return rule( + implementation = impl, + attrs = merged_attrs, + fragments = fragments, + **kwargs + ) + +def create_dep(call, attrs = {}, fragments = []): + """Combines a dependency's executable function, attributes, and fragments. + + Args: + call: the executable function + attrs: dict of required rule attrs + fragments: list of required configuration fragments + + Returns: + The struct + """ + return _create_dep(call, attrs, fragments) + +def _create_dep(call, attrs = {}, fragments = []): + return struct( + call = call, + attrs = attrs, + fragments = fragments, + ) + +def create_composite_dep(merge_func, *deps): + """Creates a dependency struct from multiple dependencies + + Args: + merge_func: The executable function to evaluate the dependencies. + *deps: The dependencies to compose provided as keyword args + + Returns: + A dependency struct + """ + merged_attrs = dict() + merged_frags = [] + for dep in deps: + merged_attrs.update(dep.attrs) + merged_frags.extend(dep.fragments) + + return _create_dep( + call = merge_func, + attrs = merged_attrs, + fragments = merged_frags, + )