| """ |
| 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:apex.bzl", "ApexInfo") |
| |
| def _arch_transition_impl(settings, attr): |
| """Implementation of arch_transition. |
| Four archs are included for mainline modules: x86, x86_64, arm and arm64. |
| """ |
| return { |
| "x86": { |
| "//command_line_option:platforms": "//build/bazel/platforms:android_x86", |
| }, |
| "x86_64": { |
| "//command_line_option:platforms": "//build/bazel/platforms:android_x86_64", |
| }, |
| "arm": { |
| "//command_line_option:platforms": "//build/bazel/platforms:android_arm", |
| }, |
| "arm64": { |
| "//command_line_option:platforms": "//build/bazel/platforms:android_arm64", |
| }, |
| } |
| |
| # Multi-arch transition. |
| arch_transition = transition( |
| implementation = _arch_transition_impl, |
| inputs = [], |
| outputs = [ |
| "//command_line_option:platforms", |
| ], |
| ) |
| |
| # Arch to ABI map |
| _arch_abi_map = { |
| "arm64": "arm64-v8a", |
| "arm": "armeabi-v7a", |
| "x86_64": "x86_64", |
| "x86": "x86", |
| } |
| |
| def _apex_proto_convert(ctx, arch, module_name, apex_file): |
| """Run 'aapt2 convert' to convert resource files to protobuf format.""" |
| # Inputs |
| inputs = [ |
| apex_file, |
| ctx.executable._aapt2, |
| ] |
| |
| # Outputs |
| filename = apex_file.basename |
| pos_dot = filename.rindex(".") |
| proto_convert_file = ctx.actions.declare_file("/".join([ |
| module_name, |
| arch, |
| filename[:pos_dot] + ".pb" + filename[pos_dot:]])) |
| outputs = [proto_convert_file] |
| |
| # Arguments |
| args = ctx.actions.args() |
| args.add_all(["convert"]) |
| args.add_all(["--output-format", "proto"]) |
| args.add_all([apex_file]) |
| args.add_all(["-o", proto_convert_file.path]) |
| |
| ctx.actions.run( |
| inputs = inputs, |
| outputs = outputs, |
| executable = ctx.executable._aapt2, |
| arguments = [args], |
| mnemonic = "ApexProtoConvert", |
| ) |
| return proto_convert_file |
| |
| def _apex_base_file(ctx, arch, module_name, apex_proto_file): |
| """Run zip2zip to transform the apex file the expected directory structure |
| with all files that will be included in the base module of aab file.""" |
| |
| # Inputs |
| inputs = [ |
| apex_proto_file, |
| ctx.executable._zip2zip, |
| ] |
| |
| # Outputs |
| base_file = ctx.actions.declare_file("/".join([module_name, arch, module_name + ".base"])) |
| outputs = [base_file] |
| |
| # Arguments |
| args = ctx.actions.args() |
| args.add_all(["-i", apex_proto_file]) |
| args.add_all(["-o", base_file]) |
| abi = _arch_abi_map[arch] |
| args.add_all([ |
| "apex_payload.img:apex/%s.img" % abi, |
| "apex_build_info.pb:apex/%s.build_info.pb" % abi, |
| "apex_manifest.json:root/apex_manifest.json", |
| "apex_manifest.pb:root/apex_manifest.pb", |
| "AndroidManifest.xml:manifest/AndroidManifest.xml", |
| "assets/NOTICE.html.gz:assets/NOTICE.html.gz", |
| ]) |
| |
| ctx.actions.run( |
| inputs = inputs, |
| outputs = outputs, |
| executable = ctx.executable._zip2zip, |
| arguments = [args], |
| mnemonic = "ApexBaseFile", |
| ) |
| return base_file |
| |
| def _build_bundle_config(ctx, arch, module_name): |
| """Create bundle_config.json as configuration for running bundletool.""" |
| file_content = { |
| "compression": { |
| "uncompressed_glob": [ |
| "apex_payload.img", |
| "apex_manifest.*", |
| ], |
| }, |
| "apex_config": {}, |
| } |
| bundle_config_file = ctx.actions.declare_file("/".join([module_name, "bundle_config.json"])) |
| ctx.actions.write(bundle_config_file, json.encode(file_content)) |
| |
| return bundle_config_file |
| |
| def _merge_base_files(ctx, module_name, base_files): |
| """Run merge_zips to merge all files created for each arch by _apex_base_file.""" |
| |
| # Inputs |
| inputs = base_files + [ctx.executable._merge_zips] |
| |
| # Outputs |
| merged_base_file = ctx.actions.declare_file(module_name + "/" + module_name + ".zip") |
| outputs = [merged_base_file] |
| |
| # Arguments |
| args = ctx.actions.args() |
| args.add_all(["--ignore-duplicates"]) |
| args.add_all([merged_base_file]) |
| args.add_all(base_files) |
| |
| ctx.actions.run( |
| inputs = inputs, |
| outputs = outputs, |
| executable = ctx.executable._merge_zips, |
| arguments = [args], |
| mnemonic = "ApexMergeBaseFiles", |
| ) |
| return merged_base_file |
| |
| def _apex_bundle(ctx, module_name, merged_base_file, bundle_config_file): |
| """Run bundletool to create the aab file.""" |
| |
| # Inputs |
| inputs = [ |
| bundle_config_file, |
| merged_base_file, |
| ctx.executable._bundletool, |
| ] |
| |
| # Outputs |
| bundle_file = ctx.actions.declare_file(module_name + "/" + module_name + ".aab") |
| outputs = [bundle_file] |
| |
| # Arguments |
| args = ctx.actions.args() |
| args.add_all(["build-bundle"]) |
| args.add_all(["--config", bundle_config_file]) |
| args.add_all(["--modules", merged_base_file]) |
| args.add_all(["--output", bundle_file]) |
| |
| ctx.actions.run( |
| inputs = inputs, |
| outputs = outputs, |
| executable = ctx.executable._bundletool, |
| arguments = [args], |
| mnemonic = "ApexBundleFile", |
| ) |
| return bundle_file |
| |
| def _apex_aab_impl(ctx): |
| """Implementation of apex_aab rule, which drives the process of creating aab |
| file from apex files created for each arch.""" |
| apex_base_files = [] |
| bundle_config_file = None |
| module_name = ctx.attr.mainline_module[0].label.name |
| for arch in ctx.split_attr.mainline_module: |
| apex_file = ctx.split_attr.mainline_module[arch].files.to_list()[0] |
| proto_convert_file = _apex_proto_convert(ctx, arch, module_name, apex_file) |
| base_file = _apex_base_file(ctx, arch, module_name, proto_convert_file) |
| apex_base_files.append(base_file) |
| # It is assumed that the bundle config is the same for all products. |
| if bundle_config_file == None: |
| bundle_config_file = _build_bundle_config(ctx, arch, module_name) |
| |
| merged_base_file = _merge_base_files(ctx, module_name, apex_base_files) |
| bundle_file = _apex_bundle(ctx, module_name, merged_base_file, bundle_config_file) |
| |
| return [DefaultInfo(files = depset([bundle_file]))] |
| |
| # apex_aab rule creates Android Apk Bundle (.aab) file of the APEX specified in mainline_module. |
| # There is no equivalent Soong module, and it is currently done in shell script by |
| # invoking Soong multiple times. |
| apex_aab = rule( |
| implementation = _apex_aab_impl, |
| attrs = { |
| "mainline_module": attr.label( |
| mandatory = True, |
| cfg = arch_transition, |
| providers = [ApexInfo], |
| doc = "The label of a mainline module target", |
| ), |
| "_allowlist_function_transition": attr.label( |
| default = "@bazel_tools//tools/allowlists/function_transition_allowlist", |
| doc = "Allow transition.", |
| ), |
| "_zipper": attr.label( |
| cfg = "host", |
| executable = True, |
| default = "@bazel_tools//tools/zip:zipper", |
| ), |
| "_aapt2": attr.label( |
| allow_single_file = True, |
| cfg = "host", |
| executable = True, |
| default = "//prebuilts/sdk/tools:linux/bin/aapt2", |
| ), |
| "_merge_zips": attr.label( |
| allow_single_file = True, |
| cfg = "host", |
| executable = True, |
| default = "//prebuilts/build-tools:linux-x86/bin/merge_zips", |
| ), |
| "_zip2zip": attr.label( |
| allow_single_file = True, |
| cfg = "host", |
| executable = True, |
| default = "//prebuilts/build-tools:linux-x86/bin/zip2zip", |
| ), |
| "_bundletool": attr.label( |
| cfg = "host", |
| executable = True, |
| default = "//prebuilts/bundletool", |
| ), |
| }, |
| ) |