aboutsummaryrefslogtreecommitdiff
path: root/rules
diff options
context:
space:
mode:
authorJingwen Chen <jingwen@google.com>2022-11-03 10:47:48 +0000
committerJingwen Chen <jingwen@google.com>2022-11-07 10:54:33 +0000
commita9d9eb6acd5a5aea80985a01344140b95a1ee3ab (patch)
tree9b752d56e4e8f4f793d5ab397f9f955220eed86d /rules
parentbab6ebf390310f3a33436b11267de84790f02316 (diff)
downloadbazel-a9d9eb6acd5a5aea80985a01344140b95a1ee3ab.tar.gz
bazel apex: add support for generating _using.txt file.
This file contains NDK function symbols dynamically used by an APEX's shared libraries. The shared cc_stub_library should be linked with the --version-script argument with the stub map file created by ndkstubgen, but isn't currently done, so the generated stub so had unversioned symbols. This CL passes the stub map into the linker args and additional linker inputs for the cc_shared_library action. Additionally, the using.txt generation script operates on a staging directory, so this CL extends the staging dir builder action to also return a directory (TreeArtifact) to be used as an input to the script. Mixed builds integration to come in a follow up CL. Test: new golden test Test: analysis test Test: presubmit Bug: 239081455 Bug: 257226023 Fixes: 257226023 Change-Id: I5e83caa43ad50646db0e7d25eed5f2d75b47535a
Diffstat (limited to 'rules')
-rw-r--r--rules/apex/BUILD2
-rw-r--r--rules/apex/apex.bzl62
-rw-r--r--rules/apex/apex_test.bzl33
-rw-r--r--rules/apex/toolchain.bzl6
-rw-r--r--rules/cc/cc_stub_library.bzl14
-rw-r--r--rules/partitions/partition.bzl7
-rw-r--r--rules/staging_dir_builder.py137
-rwxr-xr-xrules/staging_dir_builder_test.sh6
8 files changed, 188 insertions, 79 deletions
diff --git a/rules/apex/BUILD b/rules/apex/BUILD
index cf64c03e..2f1b4834 100644
--- a/rules/apex/BUILD
+++ b/rules/apex/BUILD
@@ -104,9 +104,11 @@ apex_toolchain(
avbtool = "//external/avb:avbtool",
conv_apex_manifest = "//system/apex/apexer:conv_apex_manifest",
e2fsdroid = "//external/e2fsprogs/contrib/android:e2fsdroid",
+ gen_ndk_usedby_apex = "//build/soong/scripts:gen_ndk_usedby_apex.sh",
jsonmodify = "//build/soong/scripts:jsonmodify",
manifest_fixer = "//build/soong/scripts:manifest_fixer",
mke2fs = "//external/e2fsprogs/misc:mke2fs",
+ readelf = "//prebuilts/clang/host/linux-x86:llvm-readelf",
resize2fs = "//external/e2fsprogs/resize:resize2fs",
sefcontext_compile = "//external/selinux/libselinux:sefcontext_compile",
soong_zip = "//build/soong/zip/cmd:soong_zip",
diff --git a/rules/apex/apex.bzl b/rules/apex/apex.bzl
index be67933e..40733adc 100644
--- a/rules/apex/apex.bzl
+++ b/rules/apex/apex.bzl
@@ -39,6 +39,7 @@ ApexInfo = provider(
"container_key_info": "Info of the container key provided as AndroidAppCertificateInfo.",
"package_name": "APEX package name.",
"backing_libs": "File containing libraries used by the APEX.",
+ "symbols_used_by_apex": "Symbol list used by this APEX.",
},
)
@@ -302,6 +303,15 @@ def _run_apexer(ctx, apex_toolchain):
# Arguments
args = ctx.actions.args()
args.add(file_mapping_file.path)
+
+ # NOTE: When used as inputs to another sandboxed action, this directory
+ # artifact's inner files will be made up of symlinks. Ensure that the
+ # aforementioned action handles symlinks correctly (e.g. following
+ # symlinks).
+ staging_dir = ctx.actions.declare_directory(ctx.attr.name + "_staging_dir")
+ args.add(staging_dir.path)
+
+ # start of apexer cmd
args.add(apexer_files.executable.path)
if ctx.attr._apexer_verbose[BuildSettingInfo].value:
args.add("--verbose")
@@ -358,7 +368,7 @@ def _run_apexer(ctx, apex_toolchain):
elif ctx.attr.testonly:
args.add("--test_only")
- args.add("STAGING_DIR_PLACEHOLDER")
+ args.add(staging_dir.path)
args.add(apex_output_file)
inputs = [
@@ -387,17 +397,18 @@ def _run_apexer(ctx, apex_toolchain):
ctx.actions.run(
inputs = inputs,
tools = tools,
- outputs = [apex_output_file],
+ outputs = [apex_output_file, staging_dir],
executable = ctx.executable._staging_dir_builder,
arguments = [args],
mnemonic = "Apexer",
)
- return (
- apex_output_file,
- requires_native_libs,
- provides_native_libs,
- _generate_apex_backing_file(ctx, backing_libs),
+ return struct(
+ unsigned_apex = apex_output_file,
+ requires_native_libs = requires_native_libs,
+ provides_native_libs = provides_native_libs,
+ backing_libs = _generate_apex_backing_file(ctx, backing_libs),
+ symbols_used_by_apex = _generate_symbols_used_by_apex(ctx, apex_toolchain, staging_dir),
)
# Sign a file with signapk.
@@ -454,11 +465,35 @@ def _run_apex_compression_tool(ctx, apex_toolchain, input_file, output_file_name
)
return compressed_file
+# Generate <module>_using.txt, which contains a list of versioned NDK symbols
+# dynamically linked to by this APEX's contents. This is used for coverage
+# checks.
+def _generate_symbols_used_by_apex(ctx, apex_toolchain, staging_dir):
+ symbols_used_by_apex = ctx.actions.declare_file(ctx.attr.name + "_using.txt")
+ ctx.actions.run(
+ outputs = [symbols_used_by_apex],
+ inputs = [staging_dir],
+ tools = [
+ apex_toolchain.readelf.files_to_run,
+ apex_toolchain.gen_ndk_usedby_apex.files_to_run,
+ ],
+ executable = apex_toolchain.gen_ndk_usedby_apex.files_to_run,
+ arguments = [
+ staging_dir.path,
+ apex_toolchain.readelf.files_to_run.executable.path,
+ symbols_used_by_apex.path,
+ ],
+ progress_message = "Generating dynamic NDK symbol list used by the %s apex" % ctx.attr.name,
+ mnemonic = "ApexUsingNDKSymbolsForCoverage",
+ )
+ return symbols_used_by_apex
+
# 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
- unsigned_apex, requires_native_libs, provides_native_libs, backing_file = _run_apexer(ctx, apex_toolchain)
+ apexer_outputs = _run_apexer(ctx, apex_toolchain)
+ unsigned_apex = apexer_outputs.unsigned_apex
apex_cert_info = ctx.attr.certificate[AndroidAppCertificateInfo]
private_key = apex_cert_info.pk8
@@ -473,17 +508,22 @@ def _apex_rule_impl(ctx):
_run_signapk(ctx, compressed_apex_output_file, signed_capex, private_key, public_key, "BazelCompressedApexSigning")
apex_key_info = ctx.attr.key[ApexKeyInfo]
+
return [
DefaultInfo(files = depset([signed_apex])),
ApexInfo(
signed_output = signed_apex,
unsigned_output = unsigned_apex,
- requires_native_libs = requires_native_libs,
- provides_native_libs = provides_native_libs,
+ requires_native_libs = apexer_outputs.requires_native_libs,
+ provides_native_libs = apexer_outputs.provides_native_libs,
bundle_key_info = apex_key_info,
container_key_info = apex_cert_info,
package_name = ctx.attr.package_name,
- backing_libs = backing_file,
+ backing_libs = apexer_outputs.backing_libs,
+ symbols_used_by_apex = apexer_outputs.symbols_used_by_apex,
+ ),
+ OutputGroupInfo(
+ coverage_files = [apexer_outputs.symbols_used_by_apex],
),
]
diff --git a/rules/apex/apex_test.bzl b/rules/apex/apex_test.bzl
index 755c741f..33e25a59 100644
--- a/rules/apex/apex_test.bzl
+++ b/rules/apex/apex_test.bzl
@@ -1412,6 +1412,38 @@ def _test_apex_backing_file():
return test_name
+def _apex_symbols_used_by_apex_test(ctx):
+ env = analysistest.begin(ctx)
+ target_under_test = analysistest.target_under_test(env)
+ actual = target_under_test[ApexInfo].symbols_used_by_apex
+
+ asserts.equals(env, ctx.attr.expected_path, actual.short_path)
+
+ return analysistest.end(env)
+
+apex_symbols_used_by_apex_test = analysistest.make(
+ _apex_symbols_used_by_apex_test,
+ attrs = {
+ "expected_path": attr.string(),
+ },
+)
+
+def _test_apex_symbols_used_by_apex():
+ name = "apex_with_symbols_used_by_apex"
+ test_name = name + "_test"
+
+ test_apex(
+ name = name,
+ )
+
+ apex_symbols_used_by_apex_test(
+ name = test_name,
+ target_under_test = name,
+ expected_path = "build/bazel/rules/apex/apex_with_symbols_used_by_apex_using.txt",
+ )
+
+ return test_name
+
def apex_test_suite(name):
native.test_suite(
name = name,
@@ -1445,5 +1477,6 @@ def apex_test_suite(name):
_test_apex_testonly_with_manifest(),
_test_apex_testonly_without_manifest(),
_test_apex_backing_file(),
+ _test_apex_symbols_used_by_apex(),
],
)
diff --git a/rules/apex/toolchain.bzl b/rules/apex/toolchain.bzl
index 747089f4..756357d2 100644
--- a/rules/apex/toolchain.bzl
+++ b/rules/apex/toolchain.bzl
@@ -30,6 +30,8 @@ ApexToolchainInfo = provider(
"soong_zip",
"jsonmodify",
"manifest_fixer",
+ "gen_ndk_usedby_apex",
+ "readelf",
],
)
@@ -49,6 +51,8 @@ def _apex_toolchain_impl(ctx):
soong_zip = ctx.file.soong_zip,
jsonmodify = ctx.attr.jsonmodify,
manifest_fixer = ctx.attr.manifest_fixer,
+ gen_ndk_usedby_apex = ctx.attr.gen_ndk_usedby_apex,
+ readelf = ctx.attr.readelf,
),
)
return [toolchain_info]
@@ -72,5 +76,7 @@ apex_toolchain = rule(
# and has been added to apex toolchain previously.
"soong_zip": attr.label(allow_single_file = True, cfg = "exec", executable = True, mandatory = True),
"manifest_fixer": attr.label(cfg = "exec", executable = True, mandatory = True),
+ "gen_ndk_usedby_apex": attr.label(cfg = "exec", executable = True, mandatory = True, allow_single_file = [".sh"]),
+ "readelf": attr.label(cfg = "exec", executable = True, mandatory = True, allow_single_file = True),
},
)
diff --git a/rules/cc/cc_stub_library.bzl b/rules/cc/cc_stub_library.bzl
index ecb6dbad..2f579cbb 100644
--- a/rules/cc/cc_stub_library.bzl
+++ b/rules/cc/cc_stub_library.bzl
@@ -69,6 +69,9 @@ def _cc_stub_gen_impl(ctx):
abi_symbol_list = out_abi_symbol_list,
version = ctx.attr.version,
),
+ OutputGroupInfo(
+ stub_map = [out_stub_map],
+ ),
]
cc_stub_gen = rule(
@@ -142,9 +145,18 @@ def cc_stub_library_shared(name, stubs_symbol_file, version, export_includes, so
if len(soname) == 0:
fail("For stub libraries 'soname' is mandatory and must be same as the soname of its source library.")
soname_flag = "-Wl,-soname," + soname
+ stub_map = name + "_stub_map"
+ native.filegroup(
+ name = stub_map,
+ srcs = [name + "_files"],
+ output_group = "stub_map",
+ tags = ["manual"],
+ )
+ version_script_flag = "-Wl,--version-script,$(location %s)" % stub_map
native.cc_shared_library(
name = name + "_so",
- user_link_flags = [soname_flag],
+ additional_linker_inputs = [stub_map],
+ user_link_flags = [soname_flag, version_script_flag],
roots = [name + "_root"],
features = disable_crt_link(features),
target_compatible_with = target_compatible_with,
diff --git a/rules/partitions/partition.bzl b/rules/partitions/partition.bzl
index 6bf0acaf..0e455aa8 100644
--- a/rules/partitions/partition.bzl
+++ b/rules/partitions/partition.bzl
@@ -312,6 +312,8 @@ def _partition_impl(ctx):
# It seems build_image will prepend /system to the paths when building_system_image=true
ctx.actions.write(file_mapping_file, json.encode({k.removeprefix("/system"): v.path for k, v in files.items()}))
+ staging_dir = ctx.actions.declare_directory(ctx.attr.name + "_staging_dir")
+
ctx.actions.run(
inputs = [
image_info,
@@ -326,11 +328,12 @@ def _partition_impl(ctx):
executable = ctx.executable._staging_dir_builder,
arguments = [
file_mapping_file.path,
+ staging_dir.path,
toolchain.build_image.path,
- "STAGING_DIR_PLACEHOLDER",
+ staging_dir.path,
image_info.path,
output_image.path,
- "STAGING_DIR_PLACEHOLDER",
+ staging_dir.path,
],
mnemonic = "BuildPartition",
# TODO: the /usr/bin addition is because build_image uses the du command
diff --git a/rules/staging_dir_builder.py b/rules/staging_dir_builder.py
index 6961c760..a5bcee9e 100644
--- a/rules/staging_dir_builder.py
+++ b/rules/staging_dir_builder.py
@@ -19,32 +19,26 @@ import os
import shutil
import subprocess
import sys
-import tempfile
+import argparse
-def main(argv):
- '''Build a staging directory, and then call a custom command.
+def build_staging_dir(file_mapping_path, staging_dir_path, command_argv):
+ '''Create a staging dir with provided file mapping and apply the command in the dir.
- The first argument to this script must be the path to a file containing a json
- dictionary mapping paths in the staging directory to paths to files that should
- be copied there. The rest of the arguments will be run as a separate command.
- At least one other argument must be "STAGING_DIR_PLACEHOLDER", which will be
- replaced with the path to the staging directory.
+ At least
- Example:
- staging_dir_builder file_mapping.json path/to/apexer --various-apexer-flags STAGING_DIR_PLACEHOLDER path/to/out.apex.unsigned
+ Args:
+ file_mapping_path (str): path to the file mapping json
+ staging_dir_path (str): path to the staging directory
+ command_argv (str list): the command to be executed, with the first arg as the executable
'''
- if len(argv) < 2:
- sys.exit('usage: staging_dir_builder MAPPING_FILE COMMAND...')
- if "STAGING_DIR_PLACEHOLDER" not in argv[1:]:
- sys.exit('At least one argument must be "STAGING_DIR_PLACEHOLDER"')
try:
- with open(argv[0], 'r') as f:
+ with open(file_mapping_path, 'r') as f:
file_mapping = json.load(f)
except OSError as e:
sys.exit(str(e))
except json.JSONDecodeError as e:
- sys.exit(argv[0] + ": JSON decode error: " + str(e))
+ sys.exit(file_mapping_path + ": JSON decode error: " + str(e))
# Validate and clean the file_mapping. This consists of:
# - Making sure it's a dict[str, str]
@@ -53,10 +47,10 @@ def main(argv):
# - Making sure no paths use .. to break out of the staging dir
cleaned_file_mapping = {}
if not isinstance(file_mapping, dict):
- sys.exit(argv[0] + ": expected a JSON dict[str, str]")
+ sys.exit(file_mapping_path + ": expected a JSON dict[str, str]")
for path_in_staging_dir, path_in_bazel in file_mapping.items():
if not isinstance(path_in_staging_dir, str) or not isinstance(path_in_bazel, str):
- sys.exit(argv[0] + ": expected a JSON dict[str, str]")
+ sys.exit(file_mapping_path + ": expected a JSON dict[str, str]")
path_in_staging_dir = os.path.normpath(path_in_staging_dir).lstrip('/')
if path_in_staging_dir in cleaned_file_mapping:
sys.exit("Staging dir path repeated twice: " + path_in_staging_dir)
@@ -65,57 +59,72 @@ def main(argv):
cleaned_file_mapping[path_in_staging_dir] = path_in_bazel
file_mapping = cleaned_file_mapping
- argv = argv[1:]
-
- with tempfile.TemporaryDirectory() as staging_dir:
- for path_in_staging_dir, path_in_bazel in file_mapping.items():
- path_in_staging_dir = os.path.join(staging_dir, path_in_staging_dir)
-
- # Because Bazel execution root is a symlink forest, all the input files are symlinks, these
- # include the dependency files declared in the BUILD files as well as the files declared
- # and created in the bzl files. For sandbox runs the former are two or more level symlinks and
- # latter are one level symlinks. For non-sandbox runs, the former are one level symlinks
- # and the latter are actual files. Here are some examples:
- #
- # Two level symlinks:
- # system/timezone/output_data/version/tz_version ->
- # /usr/local/google/home/...out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
- # execroot/__main__/system/timezone/output_data/version/tz_version ->
- # /usr/local/google/home/.../system/timezone/output_data/version/tz_version
- #
- # Three level symlinks:
- # bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/libcrypto.so ->
- # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
- # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/libcrypto.so ->
- # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
- # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/
- # liblibcrypto_stripped.so ->
- # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
- # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/
- # liblibcrypto_unstripped.so
- #
- # One level symlinks:
- # bazel-out/android_target-fastbuild/bin/system/timezone/apex/apex_manifest.pb ->
- # /usr/local/google/home/.../out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
- # execroot/__main__/bazel-out/android_target-fastbuild/bin/system/timezone/apex/
- # apex_manifest.pb
- if os.path.islink(path_in_bazel):
- path_in_bazel = os.readlink(path_in_bazel)
+ for path_in_staging_dir, path_in_bazel in file_mapping.items():
+ path_in_staging_dir = os.path.join(staging_dir_path, path_in_staging_dir)
- # For sandbox run these are the 2nd level symlinks and we need to resolve
- while os.path.islink(path_in_bazel) and 'execroot/__main__' in path_in_bazel:
- path_in_bazel = os.readlink(path_in_bazel)
+ # Because Bazel execution root is a symlink forest, all the input files are symlinks, these
+ # include the dependency files declared in the BUILD files as well as the files declared
+ # and created in the bzl files. For sandbox runs the former are two or more level symlinks and
+ # latter are one level symlinks. For non-sandbox runs, the former are one level symlinks
+ # and the latter are actual files. Here are some examples:
+ #
+ # Two level symlinks:
+ # system/timezone/output_data/version/tz_version ->
+ # /usr/local/google/home/...out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
+ # execroot/__main__/system/timezone/output_data/version/tz_version ->
+ # /usr/local/google/home/.../system/timezone/output_data/version/tz_version
+ #
+ # Three level symlinks:
+ # bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/libcrypto.so ->
+ # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
+ # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/libcrypto.so ->
+ # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
+ # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/
+ # liblibcrypto_stripped.so ->
+ # /usr/local/google/home/yudiliu/android/aosp/master/out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
+ # execroot/__main__/bazel-out/android_x86_64-fastbuild-ST-4ecd5e98bfdd/bin/external/boringssl/
+ # liblibcrypto_unstripped.so
+ #
+ # One level symlinks:
+ # bazel-out/android_target-fastbuild/bin/system/timezone/apex/apex_manifest.pb ->
+ # /usr/local/google/home/.../out/bazel/output_user_root/b1ed7e1e9af3ebbd1403e9cf794e4884/
+ # execroot/__main__/bazel-out/android_target-fastbuild/bin/system/timezone/apex/
+ # apex_manifest.pb
+ if os.path.islink(path_in_bazel):
+ path_in_bazel = os.readlink(path_in_bazel)
- os.makedirs(os.path.dirname(path_in_staging_dir), exist_ok=True)
- shutil.copyfile(path_in_bazel, path_in_staging_dir, follow_symlinks=False)
+ # For sandbox run these are the 2nd level symlinks and we need to resolve
+ while os.path.islink(path_in_bazel) and 'execroot/__main__' in path_in_bazel:
+ path_in_bazel = os.readlink(path_in_bazel)
- for i in range(len(argv)):
- if argv[i] == 'STAGING_DIR_PLACEHOLDER':
- argv[i] = staging_dir
+ os.makedirs(os.path.dirname(path_in_staging_dir), exist_ok=True)
+ shutil.copyfile(path_in_bazel, path_in_staging_dir, follow_symlinks=False)
- result = subprocess.run(argv)
+ result = subprocess.run(command_argv)
sys.exit(result.returncode)
+def main():
+ '''Build a staging directory, and then call a custom command.
+
+ The first argument to this script must be the path to a file containing a json
+ dictionary mapping paths in the staging directory to paths to files that should
+ be copied there. The rest of the arguments will be run as a separate command.
+
+ Example:
+ staging_dir_builder file_mapping.json path/to/staging_dir path/to/apexer --various-apexer-flags path/to/out.apex.unsigned
+ '''
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "file_mapping_path",
+ help="Path to the <staging dir path>:<bazel input path> file mapping JSON.",
+ )
+ parser.add_argument(
+ "staging_dir_path",
+ help="Path to a directory to store the staging directory content.",
+ )
+ args, command_argv = parser.parse_known_args()
+ build_staging_dir(args.file_mapping_path, args.staging_dir_path, command_argv)
+
if __name__ == '__main__':
- main(sys.argv[1:])
+ main()
diff --git a/rules/staging_dir_builder_test.sh b/rules/staging_dir_builder_test.sh
index 148e79d3..27d3505d 100755
--- a/rules/staging_dir_builder_test.sh
+++ b/rules/staging_dir_builder_test.sh
@@ -104,11 +104,15 @@ echo "/ 0 2000 0755
apexer_tool_paths=${avb_tool_path}:${avb_tool_path}:${e2fsdroid_path}:${mke2fs_path}:${resize2fs_path}:${debugfs_path}:${soong_zip_path}:${aapt2_path}:${sefcontext_compile_path}
+staging_dir=$(mktemp -d /tmp/temporary-dir.XXXXXXXX)
+trap 'rm -rf -- "${staging_dir}"' EXIT
+
#############################################
# run staging_dir_builder
#############################################
"${RUNFILES_DIR}/__main__/build/bazel/rules/staging_dir_builder" \
${staging_dir_builder_manifest_file} \
+ ${staging_dir} \
${apexer_tool_path} \
--manifest ${manifest_file} \
--file_contexts ${file_contexts_file} \
@@ -116,7 +120,7 @@ apexer_tool_paths=${avb_tool_path}:${avb_tool_path}:${e2fsdroid_path}:${mke2fs_p
--apexer_tool_path "${apexer_tool_paths}" \
--android_jar_path ${android_jar} \
--canned_fs_config ${canned_fs_config} \
- STAGING_DIR_PLACEHOLDER \
+ ${staging_dir} \
${output_file}
#############################################