• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright (C) 2021 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("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
17load("@soong_injection//apex_toolchain:constants.bzl", "default_manifest_version")
18load("//build/bazel/platforms:platform_utils.bzl", "platforms")
19load("//build/bazel/product_config:product_variables_providing_rule.bzl", "ProductVariablesInfo")
20load("//build/bazel/rules:common.bzl", "get_dep_targets")
21load("//build/bazel/rules:metadata.bzl", "MetadataFileInfo")
22load("//build/bazel/rules:prebuilt_file.bzl", "PrebuiltFileInfo")
23load("//build/bazel/rules:sh_binary.bzl", "ShBinaryInfo")
24load("//build/bazel/rules:toolchain_utils.bzl", "verify_toolchain_exists")
25load("//build/bazel/rules/android:android_app_certificate.bzl", "AndroidAppCertificateInfo", "android_app_certificate_with_default_cert")
26load("//build/bazel/rules/apex:cc.bzl", "ApexCcInfo", "ApexCcMkInfo", "apex_cc_aspect")
27load("//build/bazel/rules/apex:sdk_versions.bzl", "maybe_override_min_sdk_version")
28load("//build/bazel/rules/apex:transition.bzl", "apex_transition", "shared_lib_transition_32", "shared_lib_transition_64")
29load("//build/bazel/rules/cc:clang_tidy.bzl", "collect_deps_clang_tidy_info")
30load("//build/bazel/rules/cc:stripped_cc_common.bzl", "CcUnstrippedInfo", "StrippedCcBinaryInfo")
31load("//build/bazel/rules/common:api.bzl", "api")
32load(
33    "//build/bazel/rules/license:license_aspect.bzl",
34    "RuleLicensedDependenciesInfo",
35    "license_aspect",
36    "license_map",
37    "license_map_notice_files",
38    "license_map_to_json",
39)
40load(":apex_available.bzl", "ApexAvailableInfo", "apex_available_aspect")
41load(":apex_deps_validation.bzl", "ApexDepsInfo", "apex_deps_validation_aspect", "validate_apex_deps")
42load(":apex_info.bzl", "ApexInfo", "ApexMkInfo")
43load(":apex_key.bzl", "ApexKeyInfo")
44load(":bundle.bzl", "apex_zip_files")
45
46def _create_file_mapping(ctx):
47    """Create a file mapping for the APEX filesystem image.
48
49    This returns a Dict[File, str] where the dictionary keys are paths in the
50    apex staging dir / filesystem image, and the values are the files and other
51    metadata that should be installed there.
52
53    It also returns other data structures, such as:
54    - requires: libs that this apex depend on from other apex or the platform
55    - provides: libs that this apex provide to other apex or the platform
56    - make_modules_to_install: make module names of libs that needs to be installed onto the platform in a bundled build (LOCAL_REQUIRED_MODULES)
57    - make_files_info: metadata about this apex's payload to be used for other packaging steps.
58    """
59
60    # Dictionary mapping from paths in the apex to the files and associated metadata to be put there
61    file_mapping = {}
62    requires = {}
63    provides = {}
64    make_modules_to_install = {}
65    metadata_file_mapping = {}
66
67    # Generate a str -> str dictionary to define Make modules and variables for the
68    # packaging step in a mixed build. This is necessary as long as there are
69    # Make-derived actions that operate on bazel's outputs. If we move all Make
70    # packaging actions to Bazel, there's no need for this data flow.
71    make_files_info = {}
72
73    arch = platforms.get_target_arch(ctx.attr._platform_utils)
74    is_target_64_bit = platforms.get_target_bitness(ctx.attr._platform_utils) == 64
75
76    def add_file_mapping(install_dir, basename, bazel_file, klass, owner, arch = None, unstripped = None, metadata_file = None):
77        installed_path = paths.join(install_dir, basename)
78        if installed_path in file_mapping and file_mapping[installed_path] != bazel_file:
79            # TODO: we should figure this out and make it a failure
80            print("Warning: %s in this apex is already installed to %s, overwriting it with %s" %
81                  (file_mapping[installed_path].path, installed_path, bazel_file.path))
82        file_mapping[installed_path] = bazel_file
83        metadata_file_mapping[installed_path] = metadata_file
84
85        files_info = {
86            "built_file": bazel_file.path,
87            "class": klass,
88            "install_dir": install_dir,
89            "basename": basename,
90            "package": owner.package,
91            "make_module_name": owner.name,
92            "arch": arch,
93        }
94        if unstripped:
95            files_info["unstripped_built_file"] = unstripped.path
96        make_files_info[installed_path] = files_info
97
98    def _add_lib_files(directory, libs, arch):
99        for dep in libs:
100            apex_cc_info = dep[ApexCcInfo]
101            for lib in apex_cc_info.requires_native_libs.to_list():
102                requires[lib] = True
103            for lib in apex_cc_info.provides_native_libs.to_list():
104                provides[lib] = True
105            for lib_file in apex_cc_info.transitive_shared_libs.to_list():
106                stripped = lib_file.stripped
107                unstripped = lib_file.unstripped
108                add_file_mapping(
109                    directory,
110                    stripped.basename,
111                    stripped,
112                    "nativeSharedLib",
113                    stripped.owner,
114                    arch = arch,
115                    unstripped = unstripped,
116                    metadata_file = lib_file.metadata_file,
117                )
118
119            # For bundled builds.
120            apex_cc_mk_info = dep[ApexCcMkInfo]
121            for mk_module in apex_cc_mk_info.make_modules_to_install.to_list():
122                make_modules_to_install[mk_module] = True
123
124    if is_target_64_bit:
125        _add_lib_files("lib64", ctx.attr.native_shared_libs_64, arch)
126
127        secondary_arch = platforms.get_target_secondary_arch(ctx.attr._platform_utils)
128        if secondary_arch:
129            _add_lib_files("lib", ctx.attr.native_shared_libs_32, secondary_arch)
130    else:
131        _add_lib_files("lib", ctx.attr.native_shared_libs_32, arch)
132
133    backing_libs = []
134    for lib in file_mapping.values():
135        if lib.basename not in backing_libs:
136            backing_libs.append(lib.basename)
137    backing_libs = sorted(backing_libs)
138
139    # Handle prebuilts
140    for dep in ctx.attr.prebuilts:
141        prebuilt_file_info = dep[PrebuiltFileInfo]
142        if prebuilt_file_info.filename:
143            filename = prebuilt_file_info.filename
144        else:
145            filename = dep.label.name
146        add_file_mapping(
147            prebuilt_file_info.dir,
148            filename,
149            prebuilt_file_info.src,
150            "etc",
151            dep.label,
152            arch = arch,
153            metadata_file = dep[MetadataFileInfo].metadata_file,
154        )
155
156    # Handle binaries
157    for dep in ctx.attr.binaries:
158        if ShBinaryInfo in dep:
159            # sh_binary requires special handling on directory/filename construction.
160            sh_binary_info = dep[ShBinaryInfo]
161            if sh_binary_info:
162                directory = "bin"
163                if sh_binary_info.sub_dir:
164                    directory = paths.join("bin", sh_binary_info.sub_dir)
165
166                filename = dep.label.name
167                if sh_binary_info.filename:
168                    filename = sh_binary_info.filename
169
170                add_file_mapping(
171                    directory,
172                    filename,
173                    dep[DefaultInfo].files_to_run.executable,
174                    "shBinary",
175                    dep.label,
176                    arch = arch,
177                    metadata_file = dep[MetadataFileInfo].metadata_file,
178                )
179        elif ApexCcInfo in dep:
180            # cc_binary just takes the final executable from the runfiles.
181            add_file_mapping(
182                "bin",
183                dep.label.name,
184                dep[DefaultInfo].files_to_run.executable,
185                "nativeExecutable",
186                dep.label,
187                arch,
188                unstripped = dep[CcUnstrippedInfo].unstripped[0].files.to_list()[0],
189                metadata_file = dep[MetadataFileInfo].metadata_file,
190            )
191
192            # Add transitive shared lib deps of apex binaries to the apex.
193            if is_target_64_bit:
194                _add_lib_files("lib64", [dep], arch)
195            else:
196                _add_lib_files("lib", [dep], arch)
197
198    return (
199        file_mapping,
200        sorted(requires.keys(), key = lambda x: x.name),  # sort on just the name of the target, not package
201        sorted(provides.keys(), key = lambda x: x.name),
202        backing_libs,
203        sorted(make_modules_to_install),
204        sorted(make_files_info.values(), key = lambda x: ":".join([x["package"], x["make_module_name"], x["arch"]])),
205        metadata_file_mapping,
206    )
207
208def _add_so(label):
209    return label.name + ".so"
210
211def _add_apex_manifest_information(
212        ctx,
213        apex_toolchain,
214        requires_native_libs,
215        provides_native_libs):
216    apex_manifest_json = ctx.file.manifest
217    apex_manifest_full_json = ctx.actions.declare_file(ctx.attr.name + "_apex_manifest_full.json")
218
219    args = ctx.actions.args()
220    args.add(apex_manifest_json)
221    args.add_all(["-a", "requireNativeLibs"])
222    args.add_all(requires_native_libs, map_each = _add_so)  # e.g. turn "//foo/bar:baz" to "baz.so"
223    args.add_all(["-a", "provideNativeLibs"])
224    args.add_all(provides_native_libs, map_each = _add_so)
225
226    manifest_version = ctx.attr._override_apex_manifest_default_version[BuildSettingInfo].value
227    if not manifest_version:
228        manifest_version = default_manifest_version
229    args.add_all(["-se", "version", "0", manifest_version])
230
231    # TODO: support other optional flags like -v name and -a jniLibs
232    args.add_all(["-o", apex_manifest_full_json])
233
234    ctx.actions.run(
235        inputs = [apex_manifest_json],
236        outputs = [apex_manifest_full_json],
237        executable = apex_toolchain.jsonmodify[DefaultInfo].files_to_run,
238        arguments = [args],
239        mnemonic = "ApexManifestModify",
240    )
241
242    return apex_manifest_full_json
243
244# conv_apex_manifest - Convert the JSON APEX manifest to protobuf, which is needed by apexer.
245def _convert_apex_manifest_json_to_pb(ctx, apex_toolchain, apex_manifest_json):
246    apex_manifest_pb = ctx.actions.declare_file(ctx.attr.name + "_apex_manifest.pb")
247
248    ctx.actions.run(
249        outputs = [apex_manifest_pb],
250        inputs = [apex_manifest_json],
251        executable = apex_toolchain.conv_apex_manifest[DefaultInfo].files_to_run,
252        arguments = [
253            "proto",
254            apex_manifest_json.path,
255            "-o",
256            apex_manifest_pb.path,
257        ],
258        mnemonic = "ConvApexManifest",
259    )
260
261    return apex_manifest_pb
262
263def _generate_canned_fs_config(ctx, filepaths):
264    """Generate filesystem config.
265
266    This encodes the filemode, uid, and gid of each file in the APEX,
267    including apex_manifest.json and apex_manifest.pb.
268    NOTE: every file must have an entry.
269    """
270
271    # Ensure all paths don't start with / and are normalized
272    filepaths = [paths.normalize(f).lstrip("/") for f in filepaths]
273
274    # Soong also sorts the config lines to be consistent with bazel
275    filepaths = sorted([f for f in filepaths if f])
276
277    # First, collect a set of all the directories in the apex
278    apex_subdirs_set = {}
279    for f in filepaths:
280        d = paths.dirname(f)
281        if d != "":  # The root dir is handled manually below
282            # Make sure all the parent dirs of the current subdir are in the set, too
283            dirs = d.split("/")
284            for i in range(1, len(dirs) + 1):
285                apex_subdirs_set["/".join(dirs[:i])] = True
286
287    config_lines = []
288    config_lines.append("/ 1000 1000 0755")
289    config_lines.append("/apex_manifest.json 1000 1000 0644")
290    config_lines.append("/apex_manifest.pb 1000 1000 0644")
291
292    # Readonly if not executable. filepaths is already sorted.
293    config_lines += ["/" + f + " 1000 1000 0644" for f in filepaths if not f.startswith("bin/")]
294
295    # Mark all binaries as executable. filepaths is already sorted.
296    config_lines += ["/" + f + " 0 2000 0755" for f in filepaths if f.startswith("bin/")]
297
298    # All directories have the same permission.
299    config_lines += ["/" + d + " 0 2000 0755" for d in sorted(apex_subdirs_set.keys())]
300
301    output = ctx.actions.declare_file(ctx.attr.name + "_canned_fs_config.txt")
302
303    config_lines = "\n".join(config_lines) + "\n"
304    ctx.actions.write(output, config_lines)
305
306    if ctx.attr.canned_fs_config:
307        # Append the custom fs config content to the existing file
308        combined_output = ctx.actions.declare_file(ctx.attr.name + "_combined_canned_fs_config.txt")
309        ctx.actions.run_shell(
310            inputs = [ctx.file.canned_fs_config, output],
311            outputs = [combined_output],
312            mnemonic = "AppendCustomFsConfig",
313            command = "cat {i} {canned_fs_config} > {o}".format(
314                i = output.path,
315                o = combined_output.path,
316                canned_fs_config = ctx.file.canned_fs_config.path,
317            ),
318        )
319        output = combined_output
320
321    return output
322
323# Append an entry for apex_manifest.pb to the file_contexts file for this APEX,
324# which is either from /system/sepolicy/apex/<apexname>-file_contexts (set in
325# the apex macro) or custom file_contexts attribute value of this APEX. This
326# ensures that the manifest file is correctly labeled as system_file.
327def _generate_file_contexts(ctx):
328    file_contexts = ctx.actions.declare_file(ctx.attr.name + "-file_contexts")
329
330    ctx.actions.run_shell(
331        inputs = [ctx.file.file_contexts],
332        outputs = [file_contexts],
333        mnemonic = "GenerateApexFileContexts",
334        command = "cat {i} > {o} && echo >> {o} && echo /apex_manifest\\\\.pb u:object_r:system_file:s0 >> {o} && echo / u:object_r:system_file:s0 >> {o}"
335            .format(i = ctx.file.file_contexts.path, o = file_contexts.path),
336    )
337
338    return file_contexts
339
340# TODO(b/255592586): This can be reused by Java rules later.
341def _mark_manifest_as_test_only(ctx, apex_toolchain):
342    if ctx.file.android_manifest == None:
343        return None
344
345    android_manifest = ctx.file.android_manifest
346    dir_name = android_manifest.dirname
347    base_name = android_manifest.basename
348    android_manifest_fixed = ctx.actions.declare_file(paths.join(dir_name, "manifest_fixer", base_name))
349
350    args = ctx.actions.args()
351    args.add("--test-only")
352    args.add(android_manifest)
353    args.add(android_manifest_fixed)
354
355    ctx.actions.run(
356        inputs = [android_manifest],
357        outputs = [android_manifest_fixed],
358        executable = apex_toolchain.manifest_fixer[DefaultInfo].files_to_run,
359        arguments = [args],
360        mnemonic = "MarkAndroidManifestTestOnly",
361    )
362
363    return android_manifest_fixed
364
365# Generate <APEX>_backing.txt file which lists all libraries used by the APEX.
366def _generate_apex_backing_file(ctx, backing_libs):
367    backing_file = ctx.actions.declare_file(ctx.attr.name + "_backing.txt")
368    ctx.actions.write(
369        output = backing_file,
370        content = " ".join(backing_libs) + "\n",
371    )
372    return backing_file
373
374# Generate installed-files.txt which lists all installed files by the APEX.
375def _generate_installed_files_list(ctx, file_mapping):
376    installed_files = ctx.actions.declare_file(ctx.attr.name + "-installed-files.txt")
377    command = []
378    for device_path, bazel_file in file_mapping.items():
379        command.append("echo $(stat -L -c %%s %s) ./%s" % (bazel_file.path, device_path))
380    ctx.actions.run_shell(
381        inputs = file_mapping.values(),
382        outputs = [installed_files],
383        mnemonic = "GenerateApexInstalledFileList",
384        command = "(" + "; ".join(command) + ") | sort -nr > " + installed_files.path,
385    )
386    return installed_files
387
388def _generate_notices(ctx, apex_toolchain):
389    licensees = license_map(ctx.attr.binaries + ctx.attr.prebuilts + ctx.attr.native_shared_libs_32 + ctx.attr.native_shared_libs_64)
390    licenses_file = ctx.actions.declare_file(ctx.attr.name + "_licenses.json")
391    ctx.actions.write(licenses_file, "[\n%s\n]\n" % ",\n".join(license_map_to_json(licensees)))
392
393    # Run HTML notice file generator.
394    notice_file = ctx.actions.declare_file(ctx.attr.name + "_notice_dir/NOTICE.html.gz")
395    notice_generator = apex_toolchain.notice_generator[DefaultInfo].files_to_run
396
397    args = ctx.actions.args()
398    args.add_all(["-o", notice_file, licenses_file])
399
400    # TODO(asmundak): should we extend it with license info for self
401    # (the case when APEX itself has applicable_licenses attribute)?
402    inputs = license_map_notice_files(licensees) + [licenses_file]
403    ctx.actions.run(
404        mnemonic = "GenerateNoticeFile",
405        inputs = inputs,
406        outputs = [notice_file],
407        executable = notice_generator,
408        tools = [notice_generator],
409        arguments = [args],
410    )
411    return notice_file
412
413def _use_api_fingerprint(ctx):
414    product_vars = ctx.attr._product_variables[ProductVariablesInfo]
415    if not product_vars.Unbundled_build:
416        return False
417    if product_vars.Always_use_prebuilt_sdks:
418        return False
419    if not ctx.attr._unbundled_build_target_sdk_with_api_fingerprint[BuildSettingInfo].value:
420        return False
421    return True
422
423# apexer - generate the APEX file.
424def _run_apexer(ctx, apex_toolchain):
425    # Inputs
426    apex_key_info = ctx.attr.key[ApexKeyInfo]
427    privkey = apex_key_info.private_key
428    pubkey = apex_key_info.public_key
429    android_jar = apex_toolchain.android_jar
430
431    file_mapping, requires_native_libs, provides_native_libs, backing_libs, make_modules_to_install, make_files_info, metadata_file_mapping = _create_file_mapping(ctx)
432    canned_fs_config = _generate_canned_fs_config(ctx, file_mapping.keys())
433    file_contexts = _generate_file_contexts(ctx)
434    full_apex_manifest_json = _add_apex_manifest_information(ctx, apex_toolchain, requires_native_libs, provides_native_libs)
435    apex_manifest_pb = _convert_apex_manifest_json_to_pb(ctx, apex_toolchain, full_apex_manifest_json)
436    notices_file = _generate_notices(ctx, apex_toolchain)
437    api_fingerprint_file = None
438
439    file_mapping_file = ctx.actions.declare_file(ctx.attr.name + "_apex_file_mapping.json")
440    ctx.actions.write(file_mapping_file, json.encode({k: v.path for k, v in file_mapping.items()}))
441
442    # Outputs
443    apex_output_file = ctx.actions.declare_file(ctx.attr.name + ".apex.unsigned")
444
445    apexer_files = apex_toolchain.apexer[DefaultInfo].files_to_run
446
447    # Arguments
448    command = [ctx.executable._staging_dir_builder.path, file_mapping_file.path]
449
450    # NOTE: When used as inputs to another sandboxed action, this directory
451    # artifact's inner files will be made up of symlinks. Ensure that the
452    # aforementioned action handles symlinks correctly (e.g. following
453    # symlinks).
454    staging_dir = ctx.actions.declare_directory(ctx.attr.name + "_staging_dir")
455
456    command.append(staging_dir.path)
457
458    # start of apexer cmd
459    command.append(apexer_files.executable.path)
460    if ctx.attr._apexer_verbose[BuildSettingInfo].value:
461        command.append("--verbose")
462
463    command.append("--force")
464    command.append("--include_build_info")
465    command.extend(["--canned_fs_config", canned_fs_config.path])
466    command.extend(["--manifest", apex_manifest_pb.path])
467    command.extend(["--file_contexts", file_contexts.path])
468    command.extend(["--key", privkey.path])
469    command.extend(["--pubkey", pubkey.path])
470    command.extend(["--payload_type", "image"])
471    command.extend(["--payload_fs_type", "ext4"])
472    command.extend(["--assets_dir", notices_file.dirname])
473
474    # Override the package name, if it's expicitly specified
475    if ctx.attr.package_name:
476        command.extend(["--override_apk_package_name", ctx.attr.package_name])
477    else:
478        override_package_name = _override_manifest_package_name(ctx)
479        if override_package_name:
480            command.extend(["--override_apk_package_name", override_package_name])
481
482    if ctx.attr.logging_parent:
483        command.extend(["--logging_parent", ctx.attr.logging_parent])
484
485    use_api_fingerprint = _use_api_fingerprint(ctx)
486
487    target_sdk_version = str(api.final_or_future(api.default_app_target_sdk()))
488    if use_api_fingerprint:
489        api_fingerprint_file = ctx.file._api_fingerprint_txt
490        sdk_version_suffix = ".$(cat {})".format(api_fingerprint_file.path)
491        target_sdk_version = ctx.attr._platform_sdk_codename[BuildSettingInfo].value + sdk_version_suffix
492
493    command.extend(["--target_sdk_version", target_sdk_version])
494
495    # TODO(b/215339575): This is a super rudimentary way to convert "current" to a numerical number.
496    # Generalize this to API level handling logic in a separate Starlark utility, preferably using
497    # API level maps dumped from api_levels.go
498    min_sdk_version = ctx.attr.min_sdk_version
499    if min_sdk_version == "current":
500        min_sdk_version = "10000"
501
502    override_min_sdk_version = ctx.attr._apex_global_min_sdk_version_override[BuildSettingInfo].value
503    min_sdk_version = str(maybe_override_min_sdk_version(min_sdk_version, override_min_sdk_version))
504
505    if min_sdk_version == "10000" and use_api_fingerprint:
506        min_sdk_version = ctx.attr._platform_sdk_codename[BuildSettingInfo].value + sdk_version_suffix
507        command.append(api_fingerprint_file.path)
508    command.extend(["--min_sdk_version", min_sdk_version])
509
510    # apexer needs the list of directories containing all auxilliary tools invoked during
511    # the creation of an apex
512    avbtool_files = apex_toolchain.avbtool[DefaultInfo].files_to_run
513    e2fsdroid_files = apex_toolchain.e2fsdroid[DefaultInfo].files_to_run
514    mke2fs_files = apex_toolchain.mke2fs[DefaultInfo].files_to_run
515    resize2fs_files = apex_toolchain.resize2fs[DefaultInfo].files_to_run
516    sefcontext_compile_files = apex_toolchain.sefcontext_compile[DefaultInfo].files_to_run
517    staging_dir_builder_files = ctx.attr._staging_dir_builder[DefaultInfo].files_to_run
518    apexer_tool_paths = [
519        apex_toolchain.aapt2.dirname,
520        apexer_files.executable.dirname,
521        avbtool_files.executable.dirname,
522        e2fsdroid_files.executable.dirname,
523        mke2fs_files.executable.dirname,
524        resize2fs_files.executable.dirname,
525        sefcontext_compile_files.executable.dirname,
526    ]
527
528    command.extend(["--apexer_tool_path", ":".join(apexer_tool_paths)])
529
530    android_manifest = ctx.file.android_manifest
531    if android_manifest != None:
532        if ctx.attr.testonly:
533            android_manifest = _mark_manifest_as_test_only(ctx, apex_toolchain)
534        command.extend(["--android_manifest", android_manifest.path])
535    elif ctx.attr.testonly:
536        command.append("--test_only")
537
538    command.append(staging_dir.path)
539    command.append(apex_output_file.path)
540
541    inputs = [
542        ctx.executable._staging_dir_builder,
543        file_mapping_file,
544        canned_fs_config,
545        apex_manifest_pb,
546        file_contexts,
547        notices_file,
548        privkey,
549        pubkey,
550        android_jar,
551    ] + file_mapping.values()
552    if use_api_fingerprint:
553        inputs.append(api_fingerprint_file)
554
555    if android_manifest != None:
556        inputs.append(android_manifest)
557
558    tools = [
559        apexer_files,
560        avbtool_files,
561        e2fsdroid_files,
562        mke2fs_files,
563        resize2fs_files,
564        sefcontext_compile_files,
565        apex_toolchain.aapt2,
566        staging_dir_builder_files,
567    ]
568
569    # This is run_shell instead of run because --target_sdk_version may
570    # use the API fingerprinting file contents using bash expansion,
571    # and only run_shell can support that by executing the whole command with
572    # /bin/bash -c. Regular run would quote the --target_sdk_version value with
573    # single quotes ('--target_sdk_version=ABC.$(cat version.txt)'), preventing
574    # bash expansion.
575    ctx.actions.run_shell(
576        inputs = inputs,
577        tools = tools,
578        outputs = [apex_output_file, staging_dir],
579        command = " ".join(command),
580        mnemonic = "Apexer",
581    )
582    return struct(
583        unsigned_apex = apex_output_file,
584        requires_native_libs = requires_native_libs,
585        provides_native_libs = provides_native_libs,
586        backing_libs = _generate_apex_backing_file(ctx, backing_libs),
587        symbols_used_by_apex = _generate_symbols_used_by_apex(ctx, apex_toolchain, staging_dir),
588        java_symbols_used_by_apex = _generate_java_symbols_used_by_apex(ctx, apex_toolchain),
589        installed_files = _generate_installed_files_list(ctx, file_mapping),
590        make_modules_to_install = make_modules_to_install,
591        make_files_info = make_files_info,
592        file_mapping = file_mapping,
593        metadata_file_mapping = metadata_file_mapping,
594    )
595
596def _run_signapk(ctx, unsigned_file, signed_file, private_key, public_key, mnemonic):
597    """Sign a file with signapk."""
598
599    # Arguments
600    args = ctx.actions.args()
601    args.add_all(["-a", 4096])
602    args.add_all(["--align-file-size"])
603    args.add_all([public_key, private_key])
604    args.add_all([unsigned_file, signed_file])
605
606    ctx.actions.run(
607        inputs = [
608            unsigned_file,
609            private_key,
610            public_key,
611            ctx.executable._signapk,
612        ],
613        outputs = [signed_file],
614        executable = ctx.executable._signapk,
615        arguments = [args],
616        mnemonic = mnemonic,
617    )
618
619    return signed_file
620
621# See also getOverrideManifestPackageName
622# https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/builder.go;l=1000;drc=241e738c7156d928e9a993b15993cb3297face45
623def _override_manifest_package_name(ctx):
624    apex_name = ctx.attr.name
625    overrides = ctx.attr._product_variables[ProductVariablesInfo].ManifestPackageNameOverrides
626    if not overrides:
627        return None
628
629    matches = [o for o in overrides if o.split(":")[0] == apex_name]
630
631    if not matches:
632        return None
633
634    if len(matches) > 1:
635        fail("unexpected multiple manifest package overrides for %s, %s" % (apex_name, matches))
636
637    return matches[0].split(":")[1]
638
639# https://cs.android.com/android/platform/superproject/+/master:build/soong/android/config.go;drc=5ca657189aac546af0aafaba11bbc9c5d889eab3;l=1501
640# In Soong, we don't check whether the current apex is part of Unbundled_apps.
641# Hence, we might simplify the logic by just checking product_vars["Unbundled_build"]
642# TODO(b/271474456): Eventually we might default to unbundled mode in bazel-only mode
643# so that we don't need to check Unbundled_apps.
644def _compression_enabled(ctx):
645    product_vars = ctx.attr._product_variables[ProductVariablesInfo]
646
647    return product_vars.CompressedApex and len(product_vars.Unbundled_apps) == 0
648
649# Compress a file with apex_compression_tool.
650def _run_apex_compression_tool(ctx, apex_toolchain, input_file, output_file_name):
651    avbtool_files = apex_toolchain.avbtool[DefaultInfo].files_to_run
652    apex_compression_tool_files = apex_toolchain.apex_compression_tool[DefaultInfo].files_to_run
653
654    # Outputs
655    compressed_file = ctx.actions.declare_file(output_file_name)
656
657    # Arguments
658    args = ctx.actions.args()
659    args.add_all(["compress"])
660    tool_dirs = [apex_toolchain.soong_zip.dirname, avbtool_files.executable.dirname]
661    args.add_all(["--apex_compression_tool", ":".join(tool_dirs)])
662    args.add_all(["--input", input_file])
663    args.add_all(["--output", compressed_file])
664
665    ctx.actions.run(
666        inputs = [input_file],
667        tools = [
668            avbtool_files,
669            apex_compression_tool_files,
670            apex_toolchain.soong_zip,
671        ],
672        outputs = [compressed_file],
673        executable = apex_compression_tool_files,
674        arguments = [args],
675        mnemonic = "BazelApexCompressing",
676    )
677    return compressed_file
678
679# Generate <module>_using.txt, which contains a list of versioned NDK symbols
680# dynamically linked to by this APEX's contents. This is used for coverage
681# checks.
682def _generate_symbols_used_by_apex(ctx, apex_toolchain, staging_dir):
683    symbols_used_by_apex = ctx.actions.declare_file(ctx.attr.name + "_using.txt")
684    ctx.actions.run(
685        outputs = [symbols_used_by_apex],
686        inputs = [staging_dir],
687        tools = [
688            apex_toolchain.readelf.files_to_run,
689            apex_toolchain.gen_ndk_usedby_apex.files_to_run,
690        ],
691        executable = apex_toolchain.gen_ndk_usedby_apex.files_to_run,
692        arguments = [
693            staging_dir.path,
694            apex_toolchain.readelf.files_to_run.executable.path,
695            symbols_used_by_apex.path,
696        ],
697        progress_message = "Generating dynamic NDK symbol list used by the %s apex" % ctx.attr.name,
698        mnemonic = "ApexUsingNDKSymbolsForCoverage",
699    )
700    return symbols_used_by_apex
701
702# Generate <module>_using.xml, which contains a list of java API metadata used
703# by this APEX's contents. This is used for coverage checks.
704#
705# TODO(b/257954111): Add JARs and APKs as inputs to this action when we start
706# building Java mainline modules.
707def _generate_java_symbols_used_by_apex(ctx, apex_toolchain):
708    java_symbols_used_by_apex = ctx.actions.declare_file(ctx.attr.name + "_using.xml")
709    ctx.actions.run(
710        outputs = [java_symbols_used_by_apex],
711        inputs = [],
712        tools = [
713            apex_toolchain.dexdeps.files_to_run,
714            apex_toolchain.gen_java_usedby_apex.files_to_run,
715        ],
716        executable = apex_toolchain.gen_java_usedby_apex.files_to_run,
717        arguments = [
718            apex_toolchain.dexdeps.files_to_run.executable.path,
719            java_symbols_used_by_apex.path,
720        ],
721        progress_message = "Generating Java symbol list used by the %s apex" % ctx.attr.name,
722        mnemonic = "ApexUsingJavaSymbolsForCoverage",
723    )
724    return java_symbols_used_by_apex
725
726def _validate_apex_deps(ctx):
727    transitive_deps = depset(
728        transitive = [
729            d[ApexDepsInfo].transitive_deps
730            for d in (
731                ctx.attr.native_shared_libs_32 +
732                ctx.attr.native_shared_libs_64 +
733                ctx.attr.binaries +
734                ctx.attr.prebuilts
735            )
736        ],
737    )
738    validation_files = []
739    if not ctx.attr._unsafe_disable_apex_allowed_deps_check[BuildSettingInfo].value:
740        validation_files.append(validate_apex_deps(ctx, transitive_deps, ctx.file.allowed_apex_deps_manifest))
741
742    transitive_unvalidated_targets = []
743    transitive_invalid_targets = []
744    for _, attr_deps in get_dep_targets(ctx.attr, predicate = lambda target: ApexAvailableInfo in target).items():
745        for dep in attr_deps:
746            transitive_unvalidated_targets.append(dep[ApexAvailableInfo].transitive_unvalidated_targets)
747            transitive_invalid_targets.append(dep[ApexAvailableInfo].transitive_invalid_targets)
748
749    invalid_targets = depset(transitive = transitive_invalid_targets).to_list()
750    if len(invalid_targets) > 0:
751        invalid_targets_msg = "\n    ".join([
752            "{label}; apex_available tags: {tags}".format(label = target.label, tags = list(apex_available_tags))
753            for target, apex_available_tags in invalid_targets
754        ])
755        msg = ("`{apex_name}` apex has transitive dependencies that do not include the apex in " +
756               "their apex_available tags:\n    {invalid_targets_msg}").format(
757            apex_name = ctx.label,
758            invalid_targets_msg = invalid_targets_msg,
759        )
760        fail(msg)
761
762    transitive_unvalidated_targets_output_file = ctx.actions.declare_file(ctx.attr.name + "_unvalidated_deps.txt")
763    ctx.actions.write(
764        transitive_unvalidated_targets_output_file,
765        "\n".join([
766            str(label) + ": " + str(reason)
767            for label, reason in depset(transitive = transitive_unvalidated_targets).to_list()
768        ]),
769    )
770    return transitive_deps, transitive_unvalidated_targets_output_file, validation_files
771
772def _verify_updatability(ctx):
773    # TODO(b/274732759): Add these checks as more APEXes are converted to Bazel.
774    #
775    # Keep this in sync with build/soong/apex/apex.go#checkUpdatable.
776    #
777    # - Cannot use platform APIs.
778    # - Cannot use external VNDK libs.
779    # - Does not set future_updatable.
780
781    if not ctx.attr.min_sdk_version:
782        fail("updatable APEXes must set min_sdk_version.")
783
784def _generate_sbom(ctx, file_mapping, metadata_file_mapping, apex_file):
785    apex_filename = paths.basename(apex_file.path)
786    sbom_metadata_csv = ctx.actions.declare_file(apex_filename + "-sbom-metadata.csv")
787    command = []
788    metadata_files = []
789    sbom_metadata_csv_columns = [
790        "installed_file",
791        "module_path",
792        "soong_module_type",
793        "is_prebuilt_make_module",
794        "product_copy_files",
795        "kernel_module_copy_files",
796        "is_platform_generated",
797        "build_output_path",
798        "static_libraries",
799        "whole_static_libraries",
800        "is_static_lib",
801    ]
802    command.append("echo " + ",".join(sbom_metadata_csv_columns))
803    command.append("echo %s,%s,,,,,,%s,,," % (apex_filename, ctx.label.package, apex_file.path))
804    for installed_file, bazel_output_file in file_mapping.items():
805        if metadata_file_mapping[installed_file]:
806            metadata_files.append(metadata_file_mapping[installed_file])
807        command.append("echo %s,%s,,,,,,%s,,," % (installed_file, paths.dirname(bazel_output_file.short_path), bazel_output_file.path))
808    ctx.actions.run_shell(
809        inputs = file_mapping.values(),
810        outputs = [sbom_metadata_csv],
811        mnemonic = "GenerateSBOMMetadata",
812        command = "(" + "; ".join(command) + ") > " + sbom_metadata_csv.path,
813    )
814
815    sbom_file = ctx.actions.declare_file(apex_filename + ".spdx.json")
816    sbom_fragment_file = ctx.actions.declare_file(apex_filename + "-fragment.spdx")
817    inputs = [
818        apex_file,
819        sbom_metadata_csv,
820        ctx.executable._generate_sbom,
821    ]
822    inputs += file_mapping.values()
823    inputs += metadata_files
824
825    product_vars = ctx.attr._product_variables[ProductVariablesInfo]
826    build_fingerprint = "%s/%s/%s:%s/%s/%s:%s/%s" % (
827        product_vars.ProductBrand,
828        product_vars.DeviceProduct,
829        product_vars.DeviceName,
830        product_vars.Platform_version_name,
831        product_vars.BuildId,
832        "",
833        product_vars.TargetBuildVariant,
834        "_".join(product_vars.BuildVersionTags),
835    )
836    ctx.actions.run(
837        inputs = inputs,
838        outputs = [sbom_file, sbom_fragment_file],
839        arguments = [
840            "--output_file",
841            sbom_file.path,
842            "--metadata",
843            sbom_metadata_csv.path,
844            "--build_version",
845            build_fingerprint,
846            "--product_mfr",
847            product_vars.ProductManufacturer,
848            "--json",
849            "--unbundled_apex",
850        ],
851        mnemonic = "GenerateSBOM",
852        executable = ctx.executable._generate_sbom,
853    )
854    return [sbom_file, sbom_fragment_file]
855
856# See the APEX section in the README on how to use this rule.
857def _apex_rule_impl(ctx):
858    verify_toolchain_exists(ctx, "//build/bazel/rules/apex:apex_toolchain_type")
859    if ctx.attr.updatable:
860        _verify_updatability(ctx)
861
862    apex_toolchain = ctx.toolchains["//build/bazel/rules/apex:apex_toolchain_type"].toolchain_info
863
864    apexer_outputs = _run_apexer(ctx, apex_toolchain)
865    unsigned_apex = apexer_outputs.unsigned_apex
866
867    apex_cert_info = ctx.attr.certificate[0][AndroidAppCertificateInfo]
868    private_key = apex_cert_info.pk8
869    public_key = apex_cert_info.pem
870
871    signed_apex = ctx.actions.declare_file(ctx.attr.name + ".apex")
872    signed_capex = None
873
874    _run_signapk(ctx, unsigned_apex, signed_apex, private_key, public_key, "BazelApexSigning")
875
876    if ctx.attr.compressible and _compression_enabled(ctx):
877        compressed_apex_output_file = _run_apex_compression_tool(ctx, apex_toolchain, signed_apex, ctx.attr.name + ".capex.unsigned")
878        signed_capex = ctx.actions.declare_file(ctx.attr.name + ".capex")
879        _run_signapk(ctx, compressed_apex_output_file, signed_capex, private_key, public_key, "BazelCompressedApexSigning")
880
881    apex_key_info = ctx.attr.key[ApexKeyInfo]
882
883    arch = platforms.get_target_arch(ctx.attr._platform_utils)
884    zip_files = apex_zip_files(
885        actions = ctx.actions,
886        name = ctx.label.name,
887        tools = struct(
888            aapt2 = apex_toolchain.aapt2,
889            zip2zip = ctx.executable._zip2zip,
890            merge_zips = ctx.executable._merge_zips,
891            soong_zip = apex_toolchain.soong_zip,
892        ),
893        apex_file = signed_apex,
894        arch = arch,
895        secondary_arch = platforms.get_target_secondary_arch(ctx.attr._platform_utils),
896    )
897
898    transitive_apex_deps, transitive_unvalidated_targets_output_file, apex_deps_validation_files = _validate_apex_deps(ctx)
899
900    optional_output_groups = {}
901    if signed_capex:
902        optional_output_groups["signed_compressed_output"] = [signed_capex]
903
904    return [
905        DefaultInfo(files = depset([signed_apex])),
906        ApexInfo(
907            signed_output = signed_apex,
908            signed_compressed_output = signed_capex,
909            unsigned_output = unsigned_apex,
910            requires_native_libs = apexer_outputs.requires_native_libs,
911            provides_native_libs = apexer_outputs.provides_native_libs,
912            bundle_key_info = apex_key_info,
913            container_key_info = apex_cert_info,
914            package_name = ctx.attr.package_name,
915            backing_libs = apexer_outputs.backing_libs,
916            symbols_used_by_apex = apexer_outputs.symbols_used_by_apex,
917            installed_files = apexer_outputs.installed_files,
918            java_symbols_used_by_apex = apexer_outputs.java_symbols_used_by_apex,
919            base_file = zip_files.apex_only,
920            base_with_config_zip = zip_files.apex_with_config,
921        ),
922        OutputGroupInfo(
923            coverage_files = [apexer_outputs.symbols_used_by_apex],
924            java_coverage_files = [apexer_outputs.java_symbols_used_by_apex],
925            backing_libs = depset([apexer_outputs.backing_libs]),
926            installed_files = depset([apexer_outputs.installed_files]),
927            transitive_unvalidated_targets = depset([transitive_unvalidated_targets_output_file]),
928            apex_sbom = depset(_generate_sbom(ctx, apexer_outputs.file_mapping, apexer_outputs.metadata_file_mapping, signed_apex)),
929            capex_sbom = depset(_generate_sbom(ctx, apexer_outputs.file_mapping, apexer_outputs.metadata_file_mapping, signed_capex) if signed_capex else []),
930            _validation = apex_deps_validation_files,
931            **optional_output_groups
932        ),
933        ApexDepsInfo(transitive_deps = transitive_apex_deps),
934        ApexMkInfo(
935            make_modules_to_install = apexer_outputs.make_modules_to_install,
936            files_info = apexer_outputs.make_files_info,
937        ),
938        collect_deps_clang_tidy_info(ctx),
939    ]
940
941# These are the standard aspects that should be applied on all edges that
942# contribute to an APEX's payload.
943STANDARD_PAYLOAD_ASPECTS = [
944    license_aspect,
945    apex_available_aspect,
946    apex_deps_validation_aspect,
947]
948
949_apex = rule(
950    implementation = _apex_rule_impl,
951    attrs = {
952        # Attributes that configure the APEX container.
953        "manifest": attr.label(allow_single_file = [".json"]),
954        "android_manifest": attr.label(allow_single_file = [".xml"]),
955        "package_name": attr.string(),
956        "logging_parent": attr.string(),
957        "file_contexts": attr.label(allow_single_file = True, mandatory = True),
958        "canned_fs_config": attr.label(
959            allow_single_file = True,
960            doc = """Path to the canned fs config file for customizing file's
961uid/gid/mod/capabilities. The content of this file is appended to the
962default config, so that the custom entries are preferred.
963
964The format is /<path_or_glob> <uid> <gid> <mode> [capabilities=0x<cap>], where
965path_or_glob is a path or glob pattern for a file or set of files, uid/gid
966are numerial values of user ID and group ID, mode is octal value for the
967file mode, and cap is hexadecimal value for the capability.""",
968        ),
969        "key": attr.label(providers = [ApexKeyInfo], mandatory = True),
970        "certificate": attr.label(
971            providers = [AndroidAppCertificateInfo],
972            mandatory = True,
973            cfg = apex_transition,
974        ),
975        "min_sdk_version": attr.string(
976            default = "current",
977            doc = """The minimum SDK version that this APEX must support at minimum. This is usually set to
978the SDK version that the APEX was first introduced.
979
980When not set, defaults to "10000" (or "current").""",
981        ),
982        "updatable": attr.bool(default = True, doc = """Whether this APEX is considered updatable or not.
983
984When set to true, this will enforce additional rules for making sure that the
985APEX is truly updatable. To be updatable, min_sdk_version should be set as well."""),
986        "installable": attr.bool(default = True),
987        "compressible": attr.bool(default = False),
988        "base_apex_name": attr.string(
989            default = "",
990            doc = "The name of the base apex of this apex. For example, the AOSP variant of this apex.",
991        ),
992
993        # Attributes that contribute to the payload.
994        "native_shared_libs_32": attr.label_list(
995            providers = [ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo],
996            aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
997            cfg = shared_lib_transition_32,
998            doc = "The libs compiled for 32-bit",
999        ),
1000        "native_shared_libs_64": attr.label_list(
1001            providers = [ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo],
1002            aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
1003            cfg = shared_lib_transition_64,
1004            doc = "The libs compiled for 64-bit",
1005        ),
1006        "binaries": attr.label_list(
1007            providers = [
1008                # The dependency must produce _all_ of the providers in _one_ of these lists.
1009                [ShBinaryInfo, RuleLicensedDependenciesInfo],  # sh_binary
1010                [StrippedCcBinaryInfo, CcInfo, ApexCcInfo, ApexCcMkInfo, RuleLicensedDependenciesInfo],  # cc_binary (stripped)
1011            ],
1012            cfg = apex_transition,
1013            aspects = STANDARD_PAYLOAD_ASPECTS + [apex_cc_aspect],
1014        ),
1015        "prebuilts": attr.label_list(
1016            providers = [PrebuiltFileInfo, RuleLicensedDependenciesInfo],
1017            cfg = apex_transition,
1018            aspects = STANDARD_PAYLOAD_ASPECTS,
1019        ),
1020
1021        # Required to use apex_transition. This is an acknowledgement to the risks of memory bloat when using transitions.
1022        "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
1023
1024        # Tools that are not part of the apex_toolchain.
1025        "_staging_dir_builder": attr.label(
1026            cfg = "exec",
1027            doc = "The staging dir builder to avoid the problem where symlinks are created inside apex image.",
1028            executable = True,
1029            default = "//build/bazel/rules:staging_dir_builder",
1030        ),
1031        "_signapk": attr.label(
1032            cfg = "exec",
1033            doc = "The signapk tool.",
1034            executable = True,
1035            default = "//build/make/tools/signapk",
1036        ),
1037        "_zip2zip": attr.label(
1038            cfg = "exec",
1039            allow_single_file = True,
1040            doc = "The tool zip2zip. Used to convert apex file to the expected directory structure.",
1041            default = "//build/soong/cmd/zip2zip:zip2zip",
1042            executable = True,
1043        ),
1044        "_merge_zips": attr.label(
1045            cfg = "exec",
1046            allow_single_file = True,
1047            doc = "The tool merge_zips. Used to combine base zip and config file into a single zip for mixed build aab creation.",
1048            default = "//prebuilts/build-tools:linux-x86/bin/merge_zips",
1049            executable = True,
1050        ),
1051        "_platform_utils": attr.label(
1052            default = Label("//build/bazel/platforms:platform_utils"),
1053        ),
1054        "_generate_sbom": attr.label(
1055            cfg = "exec",
1056            doc = "SBOM generation tool",
1057            executable = True,
1058            default = "//build/make/tools/sbom:generate-sbom",
1059        ),
1060
1061        # allowed deps check
1062        "_unsafe_disable_apex_allowed_deps_check": attr.label(
1063            default = "//build/bazel/rules/apex:unsafe_disable_apex_allowed_deps_check",
1064        ),
1065        "allowed_apex_deps_manifest": attr.label(
1066            allow_single_file = True,
1067            default = "//packages/modules/common/build:allowed_deps.txt",
1068        ),
1069
1070        # Build settings.
1071        "_apexer_verbose": attr.label(
1072            default = "//build/bazel/rules/apex:apexer_verbose",
1073            doc = "If enabled, make apexer log verbosely.",
1074        ),
1075        "_override_apex_manifest_default_version": attr.label(
1076            default = "//build/bazel/rules/apex:override_apex_manifest_default_version",
1077            doc = "If specified, override 'version: 0' in apex_manifest.json with this value instead of the branch default. Non-zero versions will not be changed.",
1078        ),
1079        "_apex_global_min_sdk_version_override": attr.label(
1080            default = "//build/bazel/rules/apex:apex_global_min_sdk_version_override",
1081            doc = "If specified, override the min_sdk_version of this apex and in the transition and checks for dependencies.",
1082        ),
1083        "_product_variables": attr.label(
1084            default = "//build/bazel/product_config:product_vars",
1085        ),
1086
1087        # Api_fingerprint
1088        "_unbundled_build_target_sdk_with_api_fingerprint": attr.label(
1089            default = "//build/bazel/rules/apex:unbundled_build_target_sdk_with_api_fingerprint",
1090        ),
1091        "_platform_sdk_codename": attr.label(
1092            default = "//build/bazel/rules/apex:platform_sdk_codename",
1093        ),
1094        "_api_fingerprint_txt": attr.label(
1095            default = "//frameworks/base/api:api_fingerprint",
1096            allow_single_file = True,
1097        ),
1098    },
1099    # The apex toolchain is not mandatory so that we don't get toolchain resolution errors even
1100    # when the apex is not compatible with the current target (via target_compatible_with).
1101    toolchains = [config_common.toolchain_type("//build/bazel/rules/apex:apex_toolchain_type", mandatory = False)],
1102    fragments = ["platform"],
1103)
1104
1105def apex(
1106        name,
1107        manifest = "apex_manifest.json",
1108        android_manifest = None,
1109        file_contexts = None,
1110        key = None,
1111        certificate = None,
1112        certificate_name = None,
1113        min_sdk_version = None,
1114        updatable = True,
1115        installable = True,
1116        compressible = False,
1117        native_shared_libs_32 = [],
1118        native_shared_libs_64 = [],
1119        binaries = [],
1120        prebuilts = [],
1121        package_name = None,
1122        logging_parent = None,
1123        canned_fs_config = None,
1124        testonly = False,
1125        # TODO(b/255400736): tests are not fully supported yet.
1126        tests = [],
1127        target_compatible_with = [],
1128        **kwargs):
1129    "Bazel macro to correspond with the APEX bundle Soong module."
1130
1131    # If file_contexts is not specified, then use the default from //system/sepolicy/apex.
1132    # https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/builder.go;l=259-263;drc=b02043b84d86fe1007afef1ff012a2155172215c
1133    if file_contexts == None:
1134        file_contexts = "//system/sepolicy/apex:" + name + "-file_contexts"
1135
1136    if testonly:
1137        compressible = False
1138    elif tests:
1139        fail("Apex with tests attribute needs to be testonly.")
1140
1141    if certificate and certificate_name:
1142        fail("Cannot use both certificate_name and certificate attributes together. Use only one of them.")
1143    app_cert_name = name + "_app_certificate"
1144    if certificate_name:
1145        # use the name key in the default cert dir
1146        android_app_certificate_with_default_cert(
1147            name = app_cert_name,
1148            cert_name = certificate_name,
1149        )
1150        certificate_label = ":" + app_cert_name
1151    elif certificate:
1152        certificate_label = certificate
1153    else:
1154        # use the default testkey
1155        android_app_certificate_with_default_cert(name = app_cert_name)
1156        certificate_label = ":" + app_cert_name
1157
1158    target_compatible_with = select({
1159        "//build/bazel/platforms/os:android": [],
1160        "//conditions:default": ["@platforms//:incompatible"],
1161    }) + target_compatible_with
1162
1163    _apex(
1164        name = name,
1165        manifest = manifest,
1166        android_manifest = android_manifest,
1167        file_contexts = file_contexts,
1168        key = key,
1169        certificate = certificate_label,
1170        min_sdk_version = min_sdk_version,
1171        updatable = updatable,
1172        installable = installable,
1173        compressible = compressible,
1174        native_shared_libs_32 = native_shared_libs_32,
1175        native_shared_libs_64 = native_shared_libs_64,
1176        binaries = binaries,
1177        prebuilts = prebuilts,
1178        package_name = package_name,
1179        logging_parent = logging_parent,
1180        canned_fs_config = canned_fs_config,
1181        testonly = testonly,
1182        target_compatible_with = target_compatible_with,
1183        **kwargs
1184    )
1185