blob: 3bab563d956e64f2eb5ac2ba8a40599f43f9e8bb [file]
load(":apex_key.bzl", "ApexKeyInfo")
load(":prebuilt_etc.bzl", "PrebuiltEtcInfo")
load(":android_app_certificate.bzl", "AndroidAppCertificateInfo")
load("//build/bazel/rules/apex:transition.bzl", "apex_transition")
load("//build/bazel/rules/apex:cc.bzl", "apex_cc_aspect", "ApexCcInfo")
# Create input dir for the APEX filesystem image (as a tree artifact).
def _prepare_input_dir(ctx):
image_apex_dir = "image.apex"
input_dir = ctx.actions.declare_directory(image_apex_dir)
# apex_manifest[(image_file_dirname, image_file_basename)] = bazel_output_file
apex_manifest = {}
# Handle native_shared_libs
for dep in ctx.attr.native_shared_libs:
apex_cc_info = dep[ApexCcInfo]
# TODO: Update apex_transition to split (1:4) the deps, one for each target platform
# Then ApexCcInfo would only return a single lib_files field
for lib_file in apex_cc_info.lib_files:
apex_manifest[("lib", lib_file.basename)] = lib_file
for lib64_file in apex_cc_info.lib64_files:
apex_manifest[("lib64", lib64_file.basename)] = lib64_file
for lib_arm_file in apex_cc_info.lib_arm_files:
apex_manifest[("lib/arm", lib_arm_file.basename)] = lib_arm_file
# Handle prebuilts
for dep in ctx.attr.prebuilts:
# TODO: Support more prebuilts than just PrebuiltEtc
prebuilt_etc_info = dep[PrebuiltEtcInfo]
directory = "etc"
if prebuilt_etc_info.sub_dir != None and prebuilt_etc_info.sub_dir != "":
directory = "/".join([directory, prebuilt_etc_info.sub_dir])
if prebuilt_etc_info.filename != None and prebuilt_etc_info.filename != "":
filename = prebuilt_etc_info.filename
else:
filename = dep.label.name
apex_manifest[(directory,filename)] = prebuilt_etc_info.src
bazel_inputs = []
# Used for creating canned_fs_config, since every file and dir in the APEX are represented
# by an entry in the fs_config.
apex_subdirs = []
apex_filepaths = []
shell_commands = []
for (apex_dirname, apex_basename), bazel_out_file in apex_manifest.items():
bazel_inputs.append(bazel_out_file)
apex_filepath = "/".join([apex_dirname, apex_basename])
apex_filepaths.append(apex_filepath)
apex_subdirs.append(apex_dirname)
# Add a shell command to make the APEX image subdirectory
full_apex_dirname = "/".join([input_dir.path, apex_dirname])
shell_commands.append("mkdir -p %s" % (full_apex_dirname))
# Add a shell command to copy the Bazel built lib into the APEX image subdirectory
full_apex_filepath = "/".join([input_dir.path, apex_filepath])
shell_commands.append("cp -f %s %s" % (bazel_out_file.path, full_apex_filepath))
shell_command_string = " && ".join(shell_commands)
ctx.actions.run_shell(
inputs = bazel_inputs,
outputs = [input_dir],
mnemonic = "PrepareApexInputDir",
command = shell_command_string,
)
# Make sure subdirs are unique (Starlark doesn't support sets, so use a map hack)
apex_subdirs_set = {}
for d in apex_subdirs:
apex_subdirs_set[d] = True
# Make sure all the parent dirs of the current subdir are in the set, too
dirs = d.split("/")
for i in range(0, len(dirs)):
apex_subdirs_set["/".join(dirs[:i])] = True
return input_dir, apex_subdirs_set.keys(), apex_filepaths
# conv_apex_manifest - Convert the JSON APEX manifest to protobuf, which is needed by apexer.
def _convert_apex_manifest_json_to_pb(ctx, apex_toolchain):
apex_manifest_json = ctx.file.manifest
apex_manifest_pb = ctx.actions.declare_file("apex_manifest.pb")
ctx.actions.run(
outputs = [apex_manifest_pb],
inputs = [ctx.file.manifest],
executable = apex_toolchain.conv_apex_manifest,
arguments = [
"proto",
apex_manifest_json.path,
"-o", apex_manifest_pb.path
],
mnemonic = "ConvApexManifest"
)
return apex_manifest_pb
# Generate filesystem config. This encodes the filemode, uid, and gid of each
# file in the APEX, including apex_manifest.json and apex_manifest.pb.
#
# NOTE: every file must have an entry.
def _generate_canned_fs_config(ctx, dirs, filepaths):
canned_fs_config = ctx.actions.declare_file("canned_fs_config")
config_lines = []
config_lines += ["/ 1000 1000 0755"]
config_lines += ["/apex_manifest.json 1000 1000 0644"]
config_lines += ["/apex_manifest.pb 1000 1000 0644"]
config_lines += ["/" + filepath + " 1000 1000 0644" for filepath in filepaths]
config_lines += ["/" + d + " 0 2000 0755" for d in dirs]
ctx.actions.write(canned_fs_config, "\n".join(config_lines))
return canned_fs_config
# apexer - generate the APEX file.
def _run_apexer(ctx, apex_toolchain, input_dir, apex_manifest_pb, canned_fs_config):
# Inputs
file_contexts = ctx.file.file_contexts
apex_key_info = ctx.attr.key[ApexKeyInfo]
privkey = apex_key_info.private_key
pubkey = apex_key_info.public_key
android_jar = apex_toolchain.android_jar
android_manifest = ctx.file.android_manifest
# Outputs
apex_output = ctx.actions.declare_file(ctx.attr.name + ".apex")
# Arguments
args = ctx.actions.args()
args.add("--verbose")
args.add("--force")
args.add("--include_build_info")
args.add_all(["--manifest", apex_manifest_pb.path])
args.add_all(["--file_contexts", file_contexts.path])
args.add_all(["--canned_fs_config", canned_fs_config.path])
args.add_all(["--key", privkey.path])
args.add_all(["--pubkey", pubkey.path])
args.add_all(["--payload_type", "image"])
args.add_all(["--target_sdk_version", "10000"])
args.add_all(["--min_sdk_version", ctx.attr.min_sdk_version])
args.add_all(["--payload_fs_type", "ext4"])
if android_manifest != None:
args.add_all(["--android_manifest", android_manifest.path])
# Input dir
args.add(input_dir.path)
# Output APEX
args.add(apex_output.path)
inputs = [
input_dir,
apex_manifest_pb,
file_contexts,
canned_fs_config,
privkey,
pubkey,
android_jar,
apex_toolchain.mke2fs,
apex_toolchain.e2fsdroid,
apex_toolchain.sefcontext_compile,
apex_toolchain.resize2fs,
apex_toolchain.avbtool,
apex_toolchain.aapt2,
]
if android_manifest != None:
inputs.append(android_manifest)
ctx.actions.run(
inputs = inputs,
outputs = [apex_output],
executable = apex_toolchain.apexer,
arguments = [args],
mnemonic = "Apexer",
env = {
"APEXER_TOOL_PATH": apex_toolchain.apexer.dirname,
},
)
return apex_output
# See the APEX section in the README on how to use this rule.
def _apex_rule_impl(ctx):
apex_toolchain = ctx.toolchains["//build/bazel/rules/apex:apex_toolchain_type"].toolchain_info
input_dir, apex_subdirs, apex_filepaths = _prepare_input_dir(ctx)
apex_manifest_pb = _convert_apex_manifest_json_to_pb(ctx, apex_toolchain)
canned_fs_config = _generate_canned_fs_config(ctx, apex_subdirs, apex_filepaths)
apex_output = _run_apexer(ctx, apex_toolchain, input_dir, apex_manifest_pb, canned_fs_config)
files_to_build = depset([apex_output])
return [DefaultInfo(files = files_to_build)]
_apex = rule(
implementation = _apex_rule_impl,
attrs = {
"manifest": attr.label(allow_single_file = [".json"]),
"android_manifest": attr.label(allow_single_file = [".xml"]),
"file_contexts": attr.label(allow_single_file = True, mandatory = True),
"key": attr.label(providers = [ApexKeyInfo]),
"certificate": attr.label(providers = [AndroidAppCertificateInfo]),
"min_sdk_version": attr.string(),
"updatable": attr.bool(default = True),
"installable": attr.bool(default = True),
"native_shared_libs": attr.label_list(providers = [ApexCcInfo], aspects = [apex_cc_aspect], cfg = apex_transition),
"binaries": attr.label_list(cfg = apex_transition),
"prebuilts": attr.label_list(providers = [PrebuiltEtcInfo], cfg = apex_transition),
# Required to use apex_transition. This is an acknowledgement to the risks of memory bloat when using transitions.
"_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
},
toolchains = ["//build/bazel/rules/apex:apex_toolchain_type"],
)
def apex(
name,
manifest = "apex_manifest.json",
android_manifest = None,
file_contexts = None,
key = None,
certificate = None,
min_sdk_version = None,
updatable = True,
installable = True,
native_shared_libs = [],
binaries = [],
prebuilts = [],
**kwargs):
"Bazel macro to correspond with the APEX bundle Soong module."
# If file_contexts is not specified, then use the default from //system/sepolicy/apex.
# https://cs.android.com/android/platform/superproject/+/master:build/soong/apex/builder.go;l=259-263;drc=b02043b84d86fe1007afef1ff012a2155172215c
if file_contexts == None:
file_contexts = "//system/sepolicy/apex:" + name + "-file_contexts"
_apex(
name = name,
manifest = manifest,
android_manifest = android_manifest,
file_contexts = file_contexts,
key = key,
certificate = certificate,
min_sdk_version = min_sdk_version,
updatable = updatable,
installable = installable,
native_shared_libs = native_shared_libs,
binaries = binaries,
prebuilts = prebuilts,
**kwargs,
)