1# Copyright (C) 2022 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15load("@bazel_skylib//lib:paths.bzl", "paths")
16
17# Arch to ABI map
18_arch_abi_map = {
19    "arm": "armeabi-v7a",
20    "arm64": "arm64-v8a",
21    "x86": "x86",
22    "x86_64": "x86_64",
23}
24
25def _proto_convert(actions, name, aapt2, apex_file):
26    """Run 'aapt2 convert' to convert resource files to protobuf format."""
27
28    root, ext = paths.split_extension(apex_file.basename)
29    output_file = actions.declare_file(paths.join(
30        name,
31        root + ".pb" + ext,
32    ))
33
34    # Arguments
35    args = actions.args()
36    args.add("convert")
37    args.add("--output-format", "proto")
38    args.add(apex_file)
39    args.add("-o", output_file)
40
41    actions.run(
42        inputs = [apex_file],
43        outputs = [output_file],
44        executable = aapt2,
45        arguments = [args],
46        mnemonic = "ApexProtoConvert",
47    )
48    return output_file
49
50def _base_file(actions, name, zip2zip, arch, secondary_arch, apex_proto_file):
51    """Transforms the apex file to the expected directory structure with all files that will be included in the base module of aab file."""
52
53    output_file = actions.declare_file(name + "-base.zip")
54
55    # Arguments
56    args = actions.args()
57    args.add("-i", apex_proto_file)
58    args.add("-o", output_file)
59    abi = _arch_abi_map[arch]
60    if secondary_arch:
61        abi += "." + _arch_abi_map[secondary_arch]
62    args.add_all([
63        "apex_payload.img:apex/%s.img" % abi,
64        "apex_build_info.pb:apex/%s.build_info.pb" % abi,
65        "apex_manifest.json:root/apex_manifest.json",
66        "apex_manifest.pb:root/apex_manifest.pb",
67        "AndroidManifest.xml:manifest/AndroidManifest.xml",
68        "assets/NOTICE.html.gz:assets/NOTICE.html.gz",
69    ])
70
71    actions.run(
72        inputs = [apex_proto_file],
73        outputs = [output_file],
74        executable = zip2zip,
75        arguments = [args],
76        mnemonic = "ApexBaseFile",
77    )
78    return output_file
79
80def build_bundle_config(actions, name):
81    """Create bundle_config.json as configuration for running bundletool.
82
83    Args:
84      actions: ctx.actions from a rule, used to declare outputs and actions.
85      name: name of target creating action
86    Returns:
87      The bundle_config.json file
88    """
89    file_content = {
90        # TODO(b/257459237): Config should collect manifest names and paths of android apps if their manifest name is overridden.
91        "apex_config": {},
92        "compression": {
93            "uncompressed_glob": [
94                "apex_payload.img",
95                "apex_manifest.*",
96            ],
97        },
98    }
99    bundle_config_file = actions.declare_file(paths.join(name, "bundle_config.json"))
100
101    actions.write(bundle_config_file, json.encode(file_content))
102
103    return bundle_config_file
104
105def _merge_apex_zip_with_config(actions, name, soong_zip, merge_zips, apex_zip, apex_file):
106    # TODO(): Only used as compatibility with mixed builds
107    bundle_config = build_bundle_config(actions, name)
108    apex_config_zip = actions.declare_file(name + ".config")
109
110    args = actions.args()
111    args.add("-o", apex_config_zip)
112    args.add("-C", bundle_config.dirname)
113    args.add("-f", bundle_config)
114    actions.run(
115        inputs = [bundle_config],
116        outputs = [apex_config_zip],
117        executable = soong_zip,
118        arguments = [args],
119        mnemonic = "ApexBaseConfigZip",
120    )
121
122    merged_zip = actions.declare_file(apex_file.basename + "-base.zip")
123    merge_args = actions.args()
124    merge_args.add(merged_zip)
125    merge_args.add(apex_zip)
126    merge_args.add(apex_config_zip)
127    actions.run(
128        inputs = [apex_config_zip, apex_zip],
129        outputs = [merged_zip],
130        executable = merge_zips,
131        arguments = [merge_args],
132        mnemonic = "ApexMergeBaseFileAndConfig",
133    )
134    return merged_zip
135
136def apex_zip_files(actions, name, tools, apex_file, arch, secondary_arch):
137    """Create apex zip files used to create an APEX bundle.
138
139    Args:
140        actions: Actions, ctx.actions from a rule, used to declare outputs and actions.
141        name: string, name of the target creating the action
142        tools: struct containing fields with executables: aapt2, zip2zip, soong_zip, merge_zips
143        apex_file: File, APEX file
144        arch: string, the arch of the target configuration of the target requesting the action
145    Returns:
146        A struct with these fields:
147        apex_only: the regular "base" apex zip
148        apex_with_config: a zipfile that's identical to apex_only, but with the addition of bundle_config.json
149    """
150    apex_proto = _proto_convert(actions, name, tools.aapt2, apex_file)
151    apex_zip = _base_file(actions, name, tools.zip2zip, arch, secondary_arch, apex_proto)
152    merged_zip = _merge_apex_zip_with_config(actions, name, tools.soong_zip, tools.merge_zips, apex_zip, apex_file)
153
154    return struct(
155        apex_only = apex_zip,
156        apex_with_config = merged_zip,
157    )
158