xref: /aosp_15_r20/external/bazelbuild-rules_android/rules/bundletool.bzl (revision 9e965d6fece27a77de5377433c2f7e6999b8cc0b)
1# Copyright 2020 The Bazel Authors. All rights reserved.
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
15"""Bazel Bundletool Commands."""
16
17load(
18    "//rules:utils.bzl",
19    "ANDROID_TOOLCHAIN_TYPE",
20)
21load("@bazel_skylib//lib:paths.bzl", "paths")
22load(":common.bzl", _common = "common")
23load(":java.bzl", _java = "java")
24
25_density_mapping = {
26    "ldpi": 120,
27    "mdpi": 160,
28    "hdpi": 240,
29    "xhdpi": 320,
30    "xxhdpi": 480,
31    "xxxhdpi": 640,
32    "tvdpi": 213,
33}
34
35def _build(
36        ctx,
37        out = None,
38        modules = [],
39        config = None,
40        metadata = dict(),
41        bundletool = None,
42        host_javabase = None):
43    args = ctx.actions.args()
44    args.add("build-bundle")
45    args.add("--output", out)
46    if modules:
47        args.add_joined("--modules", modules, join_with = ",")
48    if config:
49        args.add("--config", config)
50    for path, f in metadata.items():
51        args.add("--metadata-file", "%s:%s" % (path, f.path))
52
53    _java.run(
54        ctx = ctx,
55        host_javabase = host_javabase,
56        executable = bundletool,
57        arguments = [args],
58        inputs = (
59            modules +
60            ([config] if config else []) +
61            metadata.values()
62        ),
63        outputs = [out],
64        mnemonic = "BuildBundle",
65        progress_message = "Building bundle %s" % out.short_path,
66    )
67
68def _build_device_json(
69        ctx,
70        out,
71        abis,
72        locales,
73        density,
74        sdk_version):
75    json_content = json.encode(struct(
76        supportedAbis = abis,
77        supportedLocales = locales,
78        screenDensity = _density_mapping[density],
79        sdkVersion = int(sdk_version),
80    ))
81    ctx.actions.write(out, json_content)
82
83def _build_sdk_apks(
84        ctx,
85        out = None,
86        aapt2 = None,
87        sdk_bundle = None,
88        debug_key = None,
89        bundletool = None,
90        host_javabase = None):
91    apks_out = ctx.actions.declare_directory(
92        "%s_apks_out" % paths.basename(out.path).replace(".", "_"),
93        sibling = out,
94    )
95    args = ctx.actions.args()
96    args.add("build-sdk-apks")
97    args.add("--aapt2", aapt2.executable.path)
98    args.add("--sdk-bundle", sdk_bundle)
99    args.add("--ks", debug_key)
100    args.add("--ks-pass=pass:android")
101    args.add("--ks-key-alias=androiddebugkey")
102    args.add("--key-pass=pass:android")
103    args.add("--output-format=DIRECTORY")
104    args.add("--output", apks_out.path)
105    _java.run(
106        ctx = ctx,
107        host_javabase = host_javabase,
108        executable = bundletool,
109        arguments = [args],
110        inputs = [
111            sdk_bundle,
112            debug_key,
113        ],
114        tools = [aapt2],
115        outputs = [apks_out],
116        mnemonic = "BuildSdkApksDir",
117        progress_message = "Building SDK APKs directory %s" % apks_out.short_path,
118    )
119
120    # Now move standalone APK out of bundletool output dir.
121    ctx.actions.run_shell(
122        command = """
123set -e
124APKS_OUT_DIR=%s
125DEBUG_APK_PATH=%s
126
127mv "${APKS_OUT_DIR}/standalones/standalone.apk" "${DEBUG_APK_PATH}"
128""" % (
129            apks_out.path,
130            out.path,
131        ),
132        tools = [],
133        arguments = [],
134        inputs = [apks_out],
135        outputs = [out],
136        mnemonic = "ExtractDebugSdkApk",
137        progress_message = "Extract debug SDK APK to %s" % out.short_path,
138    )
139
140def _build_sdk_bundle(
141        ctx,
142        out = None,
143        module = None,
144        sdk_api_descriptors = None,
145        sdk_modules_config = None,
146        bundletool = None,
147        host_javabase = None):
148    args = ctx.actions.args()
149    args.add("build-sdk-bundle")
150
151    args.add("--sdk-modules-config", sdk_modules_config)
152    args.add("--sdk-interface-descriptors", sdk_api_descriptors)
153    args.add("--modules", module)
154    args.add("--output", out)
155    _java.run(
156        ctx = ctx,
157        host_javabase = host_javabase,
158        executable = bundletool,
159        arguments = [args],
160        inputs = [
161            module,
162            sdk_api_descriptors,
163            sdk_modules_config,
164        ],
165        outputs = [out],
166        mnemonic = "BuildASB",
167        progress_message = "Building SDK bundle %s" % out.short_path,
168    )
169
170def _build_sdk_module(
171        ctx,
172        out = None,
173        internal_apk = None,
174        bundletool_module_builder = None,
175        host_javabase = None):
176    args = ctx.actions.args()
177    args.add("--internal_apk_path", internal_apk)
178    args.add("--output_module_path", out)
179    ctx.actions.run(
180        inputs = [internal_apk],
181        outputs = [out],
182        executable = bundletool_module_builder,
183        arguments = [args],
184        mnemonic = "BuildSdkModule",
185        progress_message = "Building ASB zip module %s" % out.short_path,
186        toolchain = ANDROID_TOOLCHAIN_TYPE,
187    )
188
189def _bundle_to_apks(
190        ctx,
191        out = None,
192        bundle = None,
193        mode = None,
194        system_apk_options = None,
195        device_spec = None,
196        keystore = None,
197        oldest_signer = None,
198        lineage = None,
199        rotation_min_sdk = None,
200        modules = None,
201        aapt2 = None,
202        bundletool = None,
203        host_javabase = None):
204    inputs = [bundle]
205    args = ctx.actions.args()
206    args.add("build-apks")
207    args.add("--output", out)
208    args.add("--bundle", bundle)
209    args.add("--aapt2", aapt2.executable.path)
210
211    if mode:
212        args.add("--mode", mode)
213
214    if system_apk_options:
215        if mode != "SYSTEM":
216            fail("Unexpected system_apk_options specified, requires SYSTEM mode but got %s" % mode)
217        args.add_joined("--system-apk-options", system_apk_options, join_with = ",")
218
219    if keystore:
220        args.add("--ks", keystore.path)
221        args.add("--ks-pass", "pass:android")
222        args.add("--ks-key-alias", "AndroidDebugKey")
223        inputs.append(keystore)
224
225    if lineage:
226        if not oldest_signer:
227            fail("Key rotation requires oldest_signer in %s" % ctx.label)
228        oldest_signer_properties = _common.create_signer_properties(ctx, oldest_signer)
229        args.add("--oldest-signer", oldest_signer_properties.path)
230        args.add("--lineage", lineage.short_path)
231        inputs.append(oldest_signer_properties)
232        inputs.append(oldest_signer)
233        inputs.append(lineage)
234
235    if rotation_min_sdk:
236        args.add("--rotation-min-sdk-version", rotation_min_sdk)
237
238    if device_spec:
239        args.add("--device-spec", device_spec)
240        inputs.append(device_spec)
241
242    if modules:
243        args.add_joined("--modules", modules, join_with = ",")
244
245    _java.run(
246        ctx = ctx,
247        host_javabase = host_javabase,
248        executable = bundletool,
249        arguments = [args],
250        inputs = inputs,
251        outputs = [out],
252        tools = [aapt2],
253        mnemonic = "BundleToApks",
254        progress_message = "Converting bundle to .apks: %s" % out.short_path,
255    )
256
257def _extract_config(
258        ctx,
259        out = None,
260        aab = None,
261        bundletool = None,
262        host_javabase = None):
263    # Need to execute as a shell script as the tool outputs to stdout
264    cmd = """
265set -e
266contents=`%s -jar %s dump config --bundle %s`
267echo "$contents" > %s
268""" % (
269        host_javabase[java_common.JavaRuntimeInfo].java_executable_exec_path,
270        bundletool.executable.path,
271        aab.path,
272        out.path,
273    )
274
275    ctx.actions.run_shell(
276        inputs = [aab],
277        outputs = [out],
278        tools = depset([bundletool.executable], transitive = [host_javabase[java_common.JavaRuntimeInfo].files]),
279        mnemonic = "ExtractBundleConfig",
280        progress_message = "Extract bundle config to %s" % out.short_path,
281        command = cmd,
282        exec_group = "android_and_java",
283    )
284
285def _extract_manifest(
286        ctx,
287        out = None,
288        aab = None,
289        module = None,
290        xpath = None,
291        bundletool = None,
292        host_javabase = None):
293    # Need to execute as a shell script as the tool outputs to stdout
294    extra_flags = []
295    if module:
296        extra_flags.append("--module " + module)
297    if xpath:
298        extra_flags.append("--xpath " + xpath)
299    cmd = """
300set -e
301contents=`%s -jar %s dump manifest --bundle %s %s`
302echo "$contents" > %s
303""" % (
304        host_javabase[java_common.JavaRuntimeInfo].java_executable_exec_path,
305        bundletool.executable.path,
306        aab.path,
307        " ".join(extra_flags),
308        out.path,
309    )
310
311    ctx.actions.run_shell(
312        inputs = [aab],
313        outputs = [out],
314        tools = depset([bundletool.executable], transitive = [host_javabase[java_common.JavaRuntimeInfo].files]),
315        mnemonic = "ExtractBundleManifest",
316        progress_message = "Extract bundle manifest to %s" % out.short_path,
317        command = cmd,
318        exec_group = "android_and_java",
319    )
320
321def _proto_apk_to_module(
322        ctx,
323        out = None,
324        proto_apk = None,
325        bundletool_module_builder = None):
326    args = ctx.actions.args()
327    args.add("--internal_apk_path", proto_apk)
328    args.add("--output_module_path", out)
329    ctx.actions.run(
330        inputs = [proto_apk],
331        outputs = [out],
332        executable = bundletool_module_builder,
333        arguments = [args],
334        mnemonic = "BuildAppModule",
335        progress_message = "Building AAB zip module %s" % out.short_path,
336        toolchain = ANDROID_TOOLCHAIN_TYPE,
337    )
338
339bundletool = struct(
340    build = _build,
341    build_device_json = _build_device_json,
342    build_sdk_apks = _build_sdk_apks,
343    build_sdk_bundle = _build_sdk_bundle,
344    build_sdk_module = _build_sdk_module,
345    bundle_to_apks = _bundle_to_apks,
346    extract_config = _extract_config,
347    extract_manifest = _extract_manifest,
348    proto_apk_to_module = _proto_apk_to_module,
349)
350