| # Copyright 2018 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. |
| |
| """Implementation.""" |
| |
| load( |
| "@rules_android//rules:acls.bzl", |
| _acls = "acls", |
| ) |
| load( |
| "@rules_android//rules:common.bzl", |
| _common = "common", |
| ) |
| load("@rules_android//rules:intellij.bzl", "intellij") |
| load( |
| "@rules_android//rules:java.bzl", |
| _java = "java", |
| ) |
| load("@rules_android//rules:providers.bzl", "AndroidLintRulesInfo") |
| load( |
| "@rules_android//rules:resources.bzl", |
| _resources = "resources", |
| ) |
| load( |
| "@rules_android//rules:utils.bzl", |
| _get_android_toolchain = "get_android_toolchain", |
| _utils = "utils", |
| ) |
| |
| RULE_PREFIX = "_aar" |
| ANDROID_MANIFEST = "AndroidManifest.xml" |
| LINT_JAR = "lint.jar" |
| _UNEXPECTED_LINT_JAR_ERROR = ( |
| "In target %s, has_lint_jar attribute is required when the aar contains " + |
| "a lint.jar file." |
| ) |
| |
| # Resources context dict fields. |
| _PROVIDERS = "providers" |
| _VALIDATION_RESULTS = "validation_results" |
| |
| def _create_aar_artifact(ctx, name): |
| return ctx.actions.declare_file("%s/%s/%s" % (RULE_PREFIX, ctx.label.name, name)) |
| |
| def _create_aar_tree_artifact(ctx, name): |
| return ctx.actions.declare_directory("%s/unzipped/%s/%s" % (RULE_PREFIX, name, ctx.label.name)) |
| |
| # Create an action to extract a file (specified by the parameter filename) from an AAR file. |
| def _extract_single_file( |
| ctx, |
| out_file, |
| aar, |
| filename, |
| unzip_tool): |
| args = ctx.actions.args() |
| args.add(aar) |
| args.add(filename) |
| args.add("-d", out_file.dirname) |
| |
| ctx.actions.run( |
| executable = unzip_tool, |
| arguments = [args], |
| inputs = [aar], |
| outputs = [out_file], |
| mnemonic = "AarFileExtractor", |
| progress_message = "Extracting %s from %s" % (filename, aar.basename), |
| ) |
| |
| def _extract_resources( |
| ctx, |
| out_resources_dir, |
| out_assets_dir, |
| aar, |
| aar_resources_extractor_tool): |
| args = ctx.actions.args() |
| args.add("--input_aar", aar) |
| args.add("--output_res_dir", out_resources_dir.path) |
| args.add("--output_assets_dir", out_assets_dir.path) |
| ctx.actions.run( |
| executable = aar_resources_extractor_tool, |
| arguments = [args], |
| inputs = [aar], |
| outputs = [out_resources_dir, out_assets_dir], |
| mnemonic = "AarResourcesExtractor", |
| progress_message = "Extracting resources and assets from %s" % aar.basename, |
| ) |
| |
| def _extract_native_libs( |
| ctx, |
| output_zip, |
| aar, |
| android_cpu, |
| aar_native_libs_zip_creator_tool): |
| args = ctx.actions.args() |
| args.add("--input_aar", aar) |
| args.add("--cpu", android_cpu) |
| args.add("--output_zip", output_zip) |
| ctx.actions.run( |
| executable = aar_native_libs_zip_creator_tool, |
| arguments = [args], |
| inputs = [aar], |
| outputs = [output_zip], |
| mnemonic = "AarNativeLibsFilter", |
| progress_message = "Filtering AAR native libs by architecture", |
| ) |
| |
| def _process_resources( |
| ctx, |
| aar, |
| package, |
| manifest, |
| deps, |
| aar_resources_extractor_tool, |
| unzip_tool): |
| # Extract resources and assets, if they exist. |
| resources = _create_aar_tree_artifact(ctx, "resources") |
| assets = _create_aar_tree_artifact(ctx, "assets") |
| _extract_resources( |
| ctx, |
| resources, |
| assets, |
| aar, |
| aar_resources_extractor_tool, |
| ) |
| |
| resources_ctx = _resources.process_starlark( |
| ctx, |
| manifest = manifest, |
| assets = [assets], |
| assets_dir = assets.path, |
| resource_files = [resources], |
| stamp_manifest = False, |
| deps = ctx.attr.deps, |
| exports = ctx.attr.exports, |
| exports_manifest = getattr(ctx.attr, "exports_manifest", True), |
| propagate_resources = _acls.in_aar_propagate_resources(str(ctx.label)), |
| |
| # Tool and Processing related inputs |
| aapt = _get_android_toolchain(ctx).aapt2.files_to_run, |
| android_jar = ctx.attr._android_sdk[AndroidSdkInfo].android_jar, |
| android_kit = _get_android_toolchain(ctx).android_kit.files_to_run, |
| busybox = _get_android_toolchain(ctx).android_resources_busybox.files_to_run, |
| java_toolchain = _common.get_java_toolchain(ctx), |
| host_javabase = _common.get_host_javabase(ctx), |
| instrument_xslt = _utils.only(_get_android_toolchain(ctx).add_g3itr_xslt.files.to_list()), |
| xsltproc = _get_android_toolchain(ctx).xsltproc_tool.files_to_run, |
| ) |
| |
| native_android_manifest = manifest |
| if not getattr(ctx.attr, "exports_manifest", True): |
| # Write an empty manifest, for the native pipeline. |
| native_android_manifest = ctx.actions.declare_file(ctx.label.name + "_aar/AndroidManifest.xml") |
| ctx.actions.write(native_android_manifest, content = """<?xml version="1.0" encoding="utf-8"?> |
| <manifest package="%s"> |
| </manifest> |
| """ % package) |
| |
| |
| return struct(**resources_ctx) |
| |
| def _extract_jars( |
| ctx, |
| out_jars_tree_artifact, |
| out_jars_params_file, |
| aar, |
| aar_embedded_jars_extractor_tool): |
| args = ctx.actions.args() |
| args.add("--input_aar", aar) |
| args.add("--output_dir", out_jars_tree_artifact.path) |
| args.add("--output_singlejar_param_file", out_jars_params_file) |
| ctx.actions.run( |
| executable = aar_embedded_jars_extractor_tool, |
| arguments = [args], |
| inputs = [aar], |
| outputs = [out_jars_tree_artifact, out_jars_params_file], |
| mnemonic = "AarEmbeddedJarsExtractor", |
| progress_message = "Extracting classes.jar and libs/*.jar from %s" % aar.basename, |
| ) |
| |
| def _merge_jars( |
| ctx, |
| out_jar, |
| jars_tree_artifact, |
| jars_param_file, |
| single_jar_tool): |
| args = ctx.actions.args() |
| args.add("--output", out_jar) |
| args.add("--dont_change_compression") |
| args.add("--normalize") |
| args.add("@" + jars_param_file.path) |
| ctx.actions.run( |
| executable = single_jar_tool, |
| arguments = [args], |
| inputs = [jars_tree_artifact, jars_param_file], |
| outputs = [out_jar], |
| mnemonic = "AarJarsMerger", |
| progress_message = "Merging AAR embedded jars", |
| ) |
| |
| def _extract_and_merge_jars( |
| ctx, |
| out_jar, |
| aar, |
| aar_embedded_jars_extractor_tool, |
| single_jar_tool): |
| """Extracts all the Jars within the AAR and produces a single jar. |
| |
| An AAR may have multiple Jar files embedded within it. This method |
| extracts and merges all Jars. |
| """ |
| jars_tree_artifact = _create_aar_tree_artifact(ctx, "jars") |
| jars_params_file = _create_aar_artifact(ctx, "jar_merging_params") |
| _extract_jars( |
| ctx, |
| jars_tree_artifact, |
| jars_params_file, |
| aar, |
| aar_embedded_jars_extractor_tool, |
| ) |
| _merge_jars( |
| ctx, |
| out_jar, |
| jars_tree_artifact, |
| jars_params_file, |
| single_jar_tool, |
| ) |
| |
| def _create_import_deps_check( |
| ctx, |
| jars_to_check, |
| declared_deps, |
| transitive_deps, |
| bootclasspath, |
| jdeps_output, |
| import_deps_checker_tool, |
| host_javabase): |
| args = ctx.actions.args() |
| args.add_all(jars_to_check, before_each = "--input") |
| args.add_all(declared_deps, before_each = "--directdep") |
| args.add_all(transitive_deps, before_each = "--classpath_entry") |
| args.add_all(bootclasspath, before_each = "--bootclasspath_entry") |
| args.add("--checking_mode=error") |
| args.add("--jdeps_output", jdeps_output) |
| args.add("--rule_label", ctx.label) |
| |
| _java.run( |
| ctx = ctx, |
| host_javabase = host_javabase, |
| executable = import_deps_checker_tool, |
| arguments = [args], |
| inputs = depset( |
| jars_to_check, |
| transitive = [ |
| declared_deps, |
| transitive_deps, |
| bootclasspath, |
| ], |
| ), |
| outputs = [jdeps_output], |
| mnemonic = "ImportDepsChecker", |
| progress_message = "Checking the completeness of the deps for %s" % jars_to_check, |
| ) |
| |
| def _process_jars( |
| ctx, |
| out_jar, |
| aar, |
| source_jar, |
| r_java, |
| deps, |
| exports, |
| enable_desugar_java8, |
| enable_imports_deps_check, |
| bootclasspath, |
| desugar_java8_extra_bootclasspath, |
| aar_embedded_jars_extractor_tool, |
| import_deps_checker_tool, |
| single_jar_tool, |
| java_toolchain, |
| host_javabase): |
| providers = [] |
| validation_results = [] |
| r_java_info = [r_java] if r_java else [] |
| |
| # An aar may have multple Jar files, extract and merge into a single jar. |
| _extract_and_merge_jars( |
| ctx, |
| out_jar, |
| aar, |
| aar_embedded_jars_extractor_tool, |
| single_jar_tool, |
| ) |
| |
| java_infos = deps + exports |
| |
| if enable_desugar_java8: |
| bootclasspath = depset(transitive = [ |
| desugar_java8_extra_bootclasspath, |
| bootclasspath, |
| ]) |
| |
| merged_java_info = java_common.merge(java_infos + r_java_info) |
| jdeps_artifact = _create_aar_artifact(ctx, "jdeps.proto") |
| _create_import_deps_check( |
| ctx, |
| [out_jar], |
| merged_java_info.compile_jars, |
| merged_java_info.transitive_compile_time_jars, |
| bootclasspath, |
| jdeps_artifact, |
| import_deps_checker_tool, |
| host_javabase, |
| ) |
| if enable_imports_deps_check: |
| validation_results.append(jdeps_artifact) |
| |
| java_info = JavaInfo( |
| out_jar, |
| compile_jar = java_common.stamp_jar( |
| actions = ctx.actions, |
| jar = out_jar, |
| target_label = ctx.label, |
| java_toolchain = java_toolchain, |
| ), |
| source_jar = source_jar, |
| neverlink = False, |
| deps = r_java_info + java_infos, # TODO(djwhang): Exports are not deps. |
| exports = |
| (r_java_info if _acls.in_aar_import_exports_r_java(str(ctx.label)) else []) + |
| java_infos, # TODO(djwhang): Deps are not exports. |
| # TODO(djwhang): AarImportTest is not expecting jdeps, enable or remove it completely |
| # jdeps = jdeps_artifact, |
| ) |
| providers.append(java_info) |
| |
| return struct( |
| java_info = java_info, |
| providers = providers, |
| validation_results = validation_results, |
| ) |
| |
| def _validate_rule( |
| ctx, |
| aar, |
| package, |
| manifest, |
| checks): |
| validation_output = ctx.actions.declare_file("%s_validation_output" % ctx.label.name) |
| |
| args = ctx.actions.args() |
| args.add("-aar", aar) |
| args.add("-label", str(ctx.label)) |
| args.add("-pkg", package) |
| args.add("-manifest", manifest) |
| if ctx.attr.has_lint_jar: |
| args.add("-has_lint_jar") |
| args.add("-output", validation_output) |
| |
| ctx.actions.run( |
| executable = checks, |
| arguments = [args], |
| inputs = [aar, manifest], |
| outputs = [validation_output], |
| mnemonic = "ValidateAAR", |
| progress_message = "Validating aar_import %s" % str(ctx.label), |
| ) |
| return validation_output |
| |
| def _process_lint_rules( |
| ctx, |
| aar, |
| unzip_tool): |
| providers = [] |
| |
| if ctx.attr.has_lint_jar: |
| lint_jar = _create_aar_artifact(ctx, LINT_JAR) |
| _extract_single_file( |
| ctx, |
| lint_jar, |
| aar, |
| LINT_JAR, |
| unzip_tool, |
| ) |
| providers.append(AndroidLintRulesInfo( |
| lint_jar = lint_jar, |
| )) |
| |
| providers.extend(_utils.collect_providers( |
| AndroidLintRulesInfo, |
| ctx.attr.exports, |
| )) |
| return providers |
| |
| def _collect_proguard( |
| ctx, |
| out_proguard, |
| aar, |
| aar_embedded_proguard_extractor): |
| args = ctx.actions.args() |
| args.add("--input_aar", aar) |
| args.add("--output_proguard_file", out_proguard) |
| ctx.actions.run( |
| executable = aar_embedded_proguard_extractor, |
| arguments = [args], |
| inputs = [aar], |
| outputs = [out_proguard], |
| mnemonic = "AarEmbeddedProguardExtractor", |
| progress_message = "Extracting proguard spec from %s" % aar.basename, |
| ) |
| transitive_proguard_specs = [] |
| for p in _utils.collect_providers(ProguardSpecProvider, ctx.attr.deps, ctx.attr.exports): |
| transitive_proguard_specs.append(p.specs) |
| return ProguardSpecProvider(depset([out_proguard], transitive = transitive_proguard_specs)) |
| |
| def impl(ctx): |
| """The rule implementation. |
| |
| Args: |
| ctx: The context. |
| |
| Returns: |
| A list of providers. |
| """ |
| providers = [] |
| validation_outputs = [] |
| |
| aar = _utils.only(ctx.files.aar) |
| unzip_tool = _get_android_toolchain(ctx).unzip_tool.files_to_run |
| package = _java.resolve_package_from_label(ctx.label, ctx.attr.package) |
| |
| # Extract the AndroidManifest.xml from the AAR. |
| android_manifest = _create_aar_artifact(ctx, ANDROID_MANIFEST) |
| _extract_single_file( |
| ctx, |
| android_manifest, |
| aar, |
| ANDROID_MANIFEST, |
| unzip_tool, |
| ) |
| |
| resources_ctx = _process_resources( |
| ctx, |
| aar = aar, |
| package = package, |
| manifest = android_manifest, |
| deps = ctx.attr.deps, |
| aar_resources_extractor_tool = |
| _get_android_toolchain(ctx).aar_resources_extractor.files_to_run, |
| unzip_tool = unzip_tool, |
| ) |
| providers.extend(resources_ctx.providers) |
| |
| merged_jar = _create_aar_artifact(ctx, "classes_and_libs_merged.jar") |
| jvm_ctx = _process_jars( |
| ctx, |
| out_jar = merged_jar, |
| aar = aar, |
| source_jar = ctx.file.srcjar, |
| deps = _utils.collect_providers(JavaInfo, ctx.attr.deps), |
| r_java = resources_ctx.r_java, |
| exports = _utils.collect_providers(JavaInfo, ctx.attr.exports), |
| enable_desugar_java8 = ctx.fragments.android.desugar_java8, |
| enable_imports_deps_check = |
| _acls.in_aar_import_deps_checker(str(ctx.label)), |
| aar_embedded_jars_extractor_tool = |
| _get_android_toolchain(ctx).aar_embedded_jars_extractor.files_to_run, |
| bootclasspath = |
| ctx.attr._java_toolchain[java_common.JavaToolchainInfo].bootclasspath, |
| desugar_java8_extra_bootclasspath = |
| _get_android_toolchain(ctx).desugar_java8_extra_bootclasspath.files, |
| import_deps_checker_tool = |
| _get_android_toolchain(ctx).import_deps_checker.files_to_run, |
| single_jar_tool = |
| ctx.attr._java_toolchain[java_common.JavaToolchainInfo].single_jar, |
| java_toolchain = |
| ctx.attr._java_toolchain[java_common.JavaToolchainInfo], |
| host_javabase = ctx.attr._host_javabase, |
| ) |
| providers.extend(jvm_ctx.providers) |
| validation_outputs.extend(jvm_ctx.validation_results) |
| |
| native_libs = _create_aar_artifact(ctx, "native_libs.zip") |
| _extract_native_libs( |
| ctx, |
| native_libs, |
| aar = aar, |
| android_cpu = ctx.fragments.android.android_cpu, |
| aar_native_libs_zip_creator_tool = |
| _get_android_toolchain(ctx).aar_native_libs_zip_creator.files_to_run, |
| ) |
| native_libs_infos = _utils.collect_providers( |
| AndroidNativeLibsInfo, |
| ctx.attr.deps, |
| ctx.attr.exports, |
| ) |
| providers.append( |
| AndroidNativeLibsInfo( |
| depset( |
| [native_libs], |
| transitive = [info.native_libs for info in native_libs_infos], |
| ), |
| ), |
| ) |
| |
| # Will be empty if there's no proguard.txt file in the aar |
| proguard_spec = _create_aar_artifact(ctx, "proguard.txt") |
| providers.append(_collect_proguard( |
| ctx, |
| proguard_spec, |
| aar, |
| _get_android_toolchain(ctx).aar_embedded_proguard_extractor.files_to_run, |
| )) |
| |
| lint_providers = _process_lint_rules( |
| ctx, |
| aar = aar, |
| unzip_tool = unzip_tool, |
| ) |
| providers.extend(lint_providers) |
| |
| validation_outputs.append(_validate_rule( |
| ctx, |
| aar = aar, |
| package = package, |
| manifest = android_manifest, |
| checks = _get_android_toolchain(ctx).aar_import_checks.files_to_run, |
| )) |
| |
| providers.append( |
| intellij.make_android_ide_info( |
| ctx, |
| java_package = _java.resolve_package_from_label(ctx.label, ctx.attr.package), |
| manifest = resources_ctx.merged_manifest, |
| defines_resources = resources_ctx.defines_resources, |
| merged_manifest = resources_ctx.merged_manifest, |
| resources_apk = resources_ctx.resources_apk, |
| r_jar = _utils.only(resources_ctx.r_java.outputs.jars) if resources_ctx.r_java else None, |
| java_info = jvm_ctx.java_info, |
| signed_apk = None, # signed_apk, always empty for aar_import |
| apks_under_test = [], # apks_under_test, always empty for aar_import |
| native_libs = dict(), # nativelibs, always empty for aar_import |
| idlclass = _get_android_toolchain(ctx).idlclass.files_to_run, |
| host_javabase = _common.get_host_javabase(ctx), |
| ), |
| ) |
| |
| providers.append(OutputGroupInfo(_validation = depset(validation_outputs))) |
| |
| # There isn't really any use case for building an aar_import target on its own, so the files to |
| # build could be empty. The R class JAR and merged JARs are added here as a sanity check for |
| # Bazel developers so that `bazel build java/com/my_aar_import` will fail if the resource |
| # processing or JAR merging steps fail. |
| files_to_build = [] |
| files_to_build.extend(resources_ctx.validation_results) # TODO(djwhang): This should be validation. |
| files_to_build.append(merged_jar) |
| |
| providers.append( |
| DefaultInfo( |
| files = depset(files_to_build), |
| runfiles = ctx.runfiles(), |
| ), |
| ) |
| |
| return providers |