• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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")
16load("//build/bazel/rules:toolchain_utils.bzl", "verify_toolchain_exists")
17load(":apex_info.bzl", "ApexInfo")
18load(":bundle.bzl", "build_bundle_config")
19
20def _arch_transition_impl(settings, _attr):
21    """Implementation of arch_transition.
22
23    Six arch products are included for mainline modules: x86, x86_64, x86_64only, arm, arm64, arm64only.
24    """
25    old_platform = str(settings["//command_line_option:platforms"][0])
26
27    # We can't use platforms alone to differentiate between x86_64 and x86_64
28    # with a secondary arch, which is significant for apex packaging that can
29    # optionally include the secondary arch's libs. That is currently determined
30    # by DeviceSecondaryArch in apex's lib inclusion logic, so we explicitly set
31    # DeviceSecondaryArch to "" for the 64bit only cases.
32
33    # TODO(b/249685973) Instead of using these __internal_x86 platforms, use
34    # the mainline_modules_<arch> android products
35    return {
36        # these key names must correspond to mainline_modules_<arch> product name suffixes.
37        "arm": {
38            "//command_line_option:platforms": old_platform + "__internal_arm",
39        },
40        "arm64": {
41            "//command_line_option:platforms": old_platform + "__internal_arm64",
42        },
43        "arm64only": {
44            "//command_line_option:platforms": old_platform + "__internal_arm64only",
45        },
46        "x86": {
47            "//command_line_option:platforms": old_platform + "__internal_x86",
48        },
49        "x86_64": {
50            "//command_line_option:platforms": old_platform + "__internal_x86_64",
51        },
52        "x86_64only": {
53            "//command_line_option:platforms": old_platform + "__internal_x86_64only",
54        },
55    }
56
57# Multi-arch transition.
58arch_transition = transition(
59    implementation = _arch_transition_impl,
60    inputs = [
61        "//command_line_option:platforms",
62    ],
63    outputs = [
64        "//command_line_option:platforms",
65    ],
66)
67
68def _merge_base_files(ctx, module_name, base_files):
69    """Run merge_zips to merge all files created for each arch by _apex_base_file."""
70    merged_base_file = ctx.actions.declare_file(module_name + "/" + module_name + ".zip")
71
72    # Arguments
73    args = ctx.actions.args()
74    args.add("--ignore-duplicates")
75    args.add(merged_base_file)
76    args.add_all(base_files)
77
78    ctx.actions.run(
79        inputs = base_files,
80        outputs = [merged_base_file],
81        executable = ctx.executable._merge_zips,
82        arguments = [args],
83        mnemonic = "ApexMergeBaseFiles",
84    )
85    return merged_base_file
86
87def _apex_bundle(ctx, module_name, merged_base_file, bundle_config_file):
88    """Run bundletool to create the aab file."""
89
90    # Outputs
91    bundle_file = ctx.actions.declare_file(module_name + "/" + module_name + ".aab")
92
93    # Arguments
94    args = ctx.actions.args()
95    args.add("build-bundle")
96    args.add_all(["--config", bundle_config_file])
97    args.add_all(["--modules", merged_base_file])
98    args.add_all(["--output", bundle_file])
99
100    ctx.actions.run(
101        inputs = [
102            bundle_config_file,
103            merged_base_file,
104        ],
105        outputs = [bundle_file],
106        executable = ctx.executable._bundletool,
107        arguments = [args],
108        mnemonic = "ApexBundleFile",
109    )
110    return bundle_file
111
112def _sign_bundle(ctx, aapt2, avbtool, module_name, bundle_file, apex_info):
113    """ Run dev_sign_bundle to sign the bundle_file."""
114
115    # Python3 interpreter for dev_sign_bundle to run other python scripts.
116    python_interpreter = ctx.toolchains["@bazel_tools//tools/python:toolchain_type"].py3_runtime.interpreter
117    if python_interpreter.basename != "python3":
118        python3 = ctx.actions.declare_file("python3")
119        ctx.actions.symlink(
120            output = python3,
121            target_file = python_interpreter,
122            is_executable = True,
123        )
124        python_interpreter = python3
125
126    # Input directory for dev_sign_bundle.
127    input_bundle_file = ctx.actions.declare_file(module_name + "/sign_bundle/input_dir/" + bundle_file.basename)
128    ctx.actions.symlink(
129        output = input_bundle_file,
130        target_file = bundle_file,
131    )
132
133    # Output directory  for dev_sign_bundle
134    output_dir = ctx.actions.declare_directory(module_name + "/sign_bundle/output_dir")
135
136    # Temporary directory for dev_sign_bundle
137    tmp_dir = ctx.actions.declare_directory(module_name + "/sign_bundle/tmp_dir")
138
139    # Jar file of prebuilts/bundletool
140    bundletool_jarfile = ctx.attr._bundletool_lib.files.to_list()[0]
141
142    # Keystore file
143    keystore_file = ctx.attr.dev_keystore.files.to_list()[0]
144
145    # ANDROID_HOST_OUT environment
146    debugfs_static = ctx.actions.declare_file(module_name + "/sign_bundle/android_host_out/bin/debugfs_static")
147    ctx.actions.symlink(
148        output = debugfs_static,
149        target_file = ctx.executable._debugfs,
150        is_executable = True,
151    )
152    fsck_erofs = ctx.actions.declare_file(module_name + "/sign_bundle/android_host_out/bin/fsck.erofs")
153    ctx.actions.symlink(
154        output = fsck_erofs,
155        target_file = ctx.executable._fsck_erofs,
156        is_executable = True,
157    )
158    signapk_jar = ctx.actions.declare_file(module_name + "/sign_bundle/android_host_out/framework/signapk.jar")
159    ctx.actions.symlink(
160        output = signapk_jar,
161        target_file = ctx.attr._signapk_jar.files.to_list()[0],
162        is_executable = False,
163    )
164    libconscrypt_openjdk_jni_so = ctx.actions.declare_file(module_name + "/sign_bundle/android_host_out/lib64/libconscrypt_openjdk_jni.so")
165    ctx.actions.symlink(
166        output = libconscrypt_openjdk_jni_so,
167        target_file = ctx.attr._libconscrypt_openjdk_jni.files.to_list()[1],
168        is_executable = False,
169    )
170
171    java_runtime = ctx.attr._java_runtime[java_common.JavaRuntimeInfo]
172
173    # Tools
174    tools = [
175        ctx.executable.dev_sign_bundle,
176        ctx.executable._deapexer,
177        ctx.executable._sign_apex,
178        ctx.executable._openssl,
179        ctx.executable._zip2zip,
180        ctx.executable._blkid,
181        aapt2,
182        avbtool.files_to_run.executable,
183        python_interpreter,
184        debugfs_static,
185        fsck_erofs,
186        bundletool_jarfile,
187        signapk_jar,
188        libconscrypt_openjdk_jni_so,
189        java_runtime.files,
190    ]
191
192    # Inputs
193    inputs = [
194        input_bundle_file,
195        keystore_file,
196        apex_info.bundle_key_info.private_key,
197        apex_info.container_key_info.pem,
198        apex_info.container_key_info.pk8,
199    ]
200
201    # Outputs
202    outputs = [output_dir, tmp_dir]
203
204    # Arguments
205    java_bin = paths.join(java_runtime.java_home, "bin")
206    args = ctx.actions.args()
207    args.add_all(["--input_dir", input_bundle_file.dirname])
208    args.add_all(["--output_dir", output_dir.path])
209    args.add_all(["--temp_dir", tmp_dir.path])
210    args.add_all(["--aapt2_path", aapt2.path])
211    args.add_all(["--bundletool_path", bundletool_jarfile.path])
212    args.add_all(["--deapexer_path", ctx.executable._deapexer.path])
213    args.add_all(["--blkid_path", ctx.executable._blkid.path])
214    args.add_all(["--debugfs_path", ctx.executable._debugfs.path])
215    args.add_all(["--java_binary_path", paths.join(java_bin, "java")])
216    args.add_all(["--apex_signer_path", ctx.executable._sign_apex])
217
218    ctx.actions.run(
219        inputs = inputs,
220        outputs = outputs,
221        executable = ctx.executable.dev_sign_bundle,
222        arguments = [args],
223        tools = tools,
224        env = {
225            # necessary for dev_sign_bundle.
226            "BAZEL_ANDROID_HOST_OUT": paths.dirname(debugfs_static.dirname),
227            "PATH": ":".join(
228                [
229                    python_interpreter.dirname,
230                    ctx.executable._deapexer.dirname,
231                    avbtool.files_to_run.executable.dirname,
232                    ctx.executable._openssl.dirname,
233                    java_bin,
234                ],
235            ),
236        },
237        mnemonic = "ApexSignBundleFile",
238    )
239
240    apks_file = ctx.actions.declare_file(module_name + "/" + module_name + ".apks")
241    cert_info_file = ctx.actions.declare_file(module_name + "/" + module_name + ".cert_info.txt")
242    ctx.actions.run_shell(
243        inputs = [output_dir],
244        outputs = [apks_file, cert_info_file],
245        command = " ".join(["cp", output_dir.path + "/" + module_name + "/*", apks_file.dirname]),
246    )
247
248    return [apks_file, cert_info_file]
249
250def _apex_aab_impl(ctx):
251    """Implementation of apex_aab rule.
252
253    This drives the process of creating aab file from apex files created for each arch."""
254    verify_toolchain_exists(ctx, "//build/bazel/rules/apex:apex_toolchain_type")
255    apex_toolchain = ctx.toolchains["//build/bazel/rules/apex:apex_toolchain_type"].toolchain_info
256
257    prefixed_apex_files = []
258    apex_base_files = []
259    bundle_config_file = None
260    module_name = ctx.attr.mainline_module[0].label.name
261    for arch in ctx.split_attr.mainline_module:
262        apex_info = ctx.split_attr.mainline_module[arch][ApexInfo]
263        apex_base_files.append(apex_info.base_file)
264
265        arch_subdir = "mainline_modules_%s" % arch
266
267        # A mapping of files to a prefix directory they should be copied to.
268        # These files will be accessible with the apex_files output_group.
269        mapping = {
270            apex_info.base_file: arch_subdir,
271            apex_info.signed_output: arch_subdir,
272            apex_info.symbols_used_by_apex: arch_subdir + "/ndk_apis_usedby_apex",
273            apex_info.backing_libs: arch_subdir + "/ndk_apis_backedby_apex",
274            apex_info.java_symbols_used_by_apex: arch_subdir + "/java_apis_usedby_apex",
275            # TODO(b/262267680): create licensetexts
276            # TODO(b/262267551): create shareprojects
277        }
278
279        # Forward the individual files for all variants in an additional output group,
280        # so dependents can easily access the multi-arch base APEX files by building
281        # this target with --output_groups=apex_files.
282        #
283        # Copy them into an arch-specific directory, since they have the same basename.
284        for _file, _dir in mapping.items():
285            _out = ctx.actions.declare_file(_dir + "/" + _file.basename)
286            ctx.actions.run_shell(
287                inputs = [_file],
288                outputs = [_out],
289                command = " ".join(["cp", _file.path, _out.path]),
290            )
291            prefixed_apex_files.append(_out)
292
293    # Create .aab file
294    bundle_config_file = build_bundle_config(ctx.actions, ctx.label.name)
295    merged_base_file = _merge_base_files(ctx, module_name, apex_base_files)
296    bundle_file = _apex_bundle(ctx, module_name, merged_base_file, bundle_config_file)
297
298    # Create .apks file
299    apex_info = ctx.attr.mainline_module[0][ApexInfo]
300    package_name = apex_info.package_name
301
302    if ctx.attr.dev_sign_bundle and ctx.attr.dev_keystore and (package_name.startswith("com.google.android") or package_name.startswith("com.google.mainline")):
303        signed_files = _sign_bundle(ctx, apex_toolchain.aapt2, apex_toolchain.avbtool, module_name, bundle_file, apex_info)
304        return [
305            DefaultInfo(files = depset([bundle_file] + signed_files)),
306            OutputGroupInfo(apex_files = depset(prefixed_apex_files), signed_files = signed_files),
307        ]
308
309    return [
310        DefaultInfo(files = depset([bundle_file])),
311        OutputGroupInfo(apex_files = depset(prefixed_apex_files)),
312    ]
313
314# apex_aab rule creates multi-arch outputs of a Mainline module, such as the
315# Android Apk Bundle (.aab) file of the APEX specified in mainline_module.
316# There is no equivalent Soong module, and it is currently done in shell script
317# by invoking Soong multiple times.
318_apex_aab = rule(
319    implementation = _apex_aab_impl,
320    toolchains = [
321        # The apex toolchain is not mandatory so that we don't get toolchain resolution errors
322        # even when the aab is not compatible with the current target (via target_compatible_with).
323        config_common.toolchain_type("//build/bazel/rules/apex:apex_toolchain_type", mandatory = False),
324        "@bazel_tools//tools/python:toolchain_type",
325    ],
326    attrs = {
327        "dev_keystore": attr.label(
328            cfg = "exec",
329            executable = False,
330        ),
331        "dev_sign_bundle": attr.label(
332            cfg = "exec",
333            executable = True,
334        ),
335        "mainline_module": attr.label(
336            mandatory = True,
337            cfg = arch_transition,
338            providers = [ApexInfo],
339            doc = "The label of a mainline module target",
340        ),
341        "_allowlist_function_transition": attr.label(
342            default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
343            doc = "Allow transition.",
344        ),
345        "_blkid": attr.label(
346            cfg = "exec",
347            executable = True,
348            default = "//external/e2fsprogs/misc:blkid",
349        ),
350        "_bundletool": attr.label(
351            cfg = "exec",
352            executable = True,
353            default = "//prebuilts/bundletool",
354        ),
355        "_bundletool_lib": attr.label(
356            cfg = "exec",
357            executable = False,
358            default = "//prebuilts/bundletool:bundletool-lib",
359        ),
360        "_deapexer": attr.label(
361            cfg = "exec",
362            executable = True,
363            default = "//system/apex/tools:deapexer",
364        ),
365        "_debugfs": attr.label(
366            cfg = "exec",
367            executable = True,
368            default = "//external/e2fsprogs/debugfs:debugfs_static",
369        ),
370        "_fsck_erofs": attr.label(
371            cfg = "exec",
372            executable = True,
373            default = "//external/erofs-utils:fsck.erofs",
374        ),
375        "_java_runtime": attr.label(
376            default = Label("@bazel_tools//tools/jdk:current_java_runtime"),
377            cfg = "exec",
378            providers = [java_common.JavaRuntimeInfo],
379        ),
380        "_libconscrypt_openjdk_jni": attr.label(
381            cfg = "exec",
382            executable = False,
383            default = "//external/conscrypt:libconscrypt_openjdk_jni",
384        ),
385        "_merge_zips": attr.label(
386            allow_single_file = True,
387            cfg = "exec",
388            executable = True,
389            default = "//prebuilts/build-tools:linux-x86/bin/merge_zips",
390        ),
391        "_openssl": attr.label(
392            allow_single_file = True,
393            cfg = "exec",
394            executable = True,
395            default = "//prebuilts/build-tools:linux-x86/bin/openssl",
396        ),
397        "_sign_apex": attr.label(
398            cfg = "exec",
399            executable = True,
400            default = "//build/make/tools/releasetools:sign_apex",
401        ),
402        "_signapk_jar": attr.label(
403            cfg = "exec",
404            executable = False,
405            default = "//build/bazel/rules/apex:signapk_deploy_jar",
406        ),
407        "_zip2zip": attr.label(
408            allow_single_file = True,
409            cfg = "exec",
410            executable = True,
411            default = "//build/soong/cmd/zip2zip:zip2zip",
412        ),
413        "_zipper": attr.label(
414            cfg = "exec",
415            executable = True,
416            default = "@bazel_tools//tools/zip:zipper",
417        ),
418    },
419)
420
421def apex_aab(name, mainline_module, dev_sign_bundle = None, dev_keystore = None, target_compatible_with = [], **kwargs):
422    target_compatible_with = select({
423        "//build/bazel/platforms/os:android": [],
424        "//conditions:default": ["@platforms//:incompatible"],
425    }) + target_compatible_with
426
427    _apex_aab(
428        name = name,
429        mainline_module = mainline_module,
430        dev_sign_bundle = dev_sign_bundle,
431        dev_keystore = dev_keystore,
432        target_compatible_with = target_compatible_with,
433        **kwargs
434    )
435