Merge "Adds support for optionally generating vbmeta.img in merge_builds."
This commit is contained in:
@@ -391,28 +391,6 @@ def AddUserdata(output_zip):
|
|||||||
img.Write()
|
img.Write()
|
||||||
|
|
||||||
|
|
||||||
def AppendVBMetaArgsForPartition(cmd, partition, image):
|
|
||||||
"""Appends the VBMeta arguments for partition.
|
|
||||||
|
|
||||||
It sets up the VBMeta argument by including the partition descriptor from the
|
|
||||||
given 'image', or by configuring the partition as a chained partition.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
cmd: A list of command args that will be used to generate the vbmeta image.
|
|
||||||
The argument for the partition will be appended to the list.
|
|
||||||
partition: The name of the partition (e.g. "system").
|
|
||||||
image: The path to the partition image.
|
|
||||||
"""
|
|
||||||
# Check if chain partition is used.
|
|
||||||
key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
|
|
||||||
if key_path:
|
|
||||||
chained_partition_arg = common.GetAvbChainedPartitionArg(
|
|
||||||
partition, OPTIONS.info_dict)
|
|
||||||
cmd.extend(["--chain_partition", chained_partition_arg])
|
|
||||||
else:
|
|
||||||
cmd.extend(["--include_descriptors_from_image", image])
|
|
||||||
|
|
||||||
|
|
||||||
def AddVBMeta(output_zip, partitions, name, needed_partitions):
|
def AddVBMeta(output_zip, partitions, name, needed_partitions):
|
||||||
"""Creates a VBMeta image and stores it in output_zip.
|
"""Creates a VBMeta image and stores it in output_zip.
|
||||||
|
|
||||||
@@ -442,45 +420,7 @@ def AddVBMeta(output_zip, partitions, name, needed_partitions):
|
|||||||
logger.info("%s.img already exists; not rebuilding...", name)
|
logger.info("%s.img already exists; not rebuilding...", name)
|
||||||
return img.name
|
return img.name
|
||||||
|
|
||||||
avbtool = OPTIONS.info_dict["avb_avbtool"]
|
common.BuildVBMeta(img.name, partitions, name, needed_partitions)
|
||||||
cmd = [avbtool, "make_vbmeta_image", "--output", img.name]
|
|
||||||
common.AppendAVBSigningArgs(cmd, name)
|
|
||||||
|
|
||||||
for partition, path in partitions.items():
|
|
||||||
if partition not in needed_partitions:
|
|
||||||
continue
|
|
||||||
assert (partition in common.AVB_PARTITIONS or
|
|
||||||
partition in common.AVB_VBMETA_PARTITIONS), \
|
|
||||||
'Unknown partition: {}'.format(partition)
|
|
||||||
assert os.path.exists(path), \
|
|
||||||
'Failed to find {} for {}'.format(path, partition)
|
|
||||||
AppendVBMetaArgsForPartition(cmd, partition, path)
|
|
||||||
|
|
||||||
args = OPTIONS.info_dict.get("avb_{}_args".format(name))
|
|
||||||
if args and args.strip():
|
|
||||||
split_args = shlex.split(args)
|
|
||||||
for index, arg in enumerate(split_args[:-1]):
|
|
||||||
# Sanity check that the image file exists. Some images might be defined
|
|
||||||
# as a path relative to source tree, which may not be available at the
|
|
||||||
# same location when running this script (we have the input target_files
|
|
||||||
# zip only). For such cases, we additionally scan other locations (e.g.
|
|
||||||
# IMAGES/, RADIO/, etc) before bailing out.
|
|
||||||
if arg == '--include_descriptors_from_image':
|
|
||||||
image_path = split_args[index + 1]
|
|
||||||
if os.path.exists(image_path):
|
|
||||||
continue
|
|
||||||
found = False
|
|
||||||
for dir_name in ['IMAGES', 'RADIO', 'PREBUILT_IMAGES']:
|
|
||||||
alt_path = os.path.join(
|
|
||||||
OPTIONS.input_tmp, dir_name, os.path.basename(image_path))
|
|
||||||
if os.path.exists(alt_path):
|
|
||||||
split_args[index + 1] = alt_path
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
assert found, 'Failed to find {}'.format(image_path)
|
|
||||||
cmd.extend(split_args)
|
|
||||||
|
|
||||||
common.RunAndCheckOutput(cmd)
|
|
||||||
img.Write()
|
img.Write()
|
||||||
return img.name
|
return img.name
|
||||||
|
|
||||||
|
@@ -625,6 +625,33 @@ def AppendAVBSigningArgs(cmd, partition):
|
|||||||
cmd.extend(["--salt", avb_salt])
|
cmd.extend(["--salt", avb_salt])
|
||||||
|
|
||||||
|
|
||||||
|
def GetAvbPartitionArg(partition, image, info_dict = None):
|
||||||
|
"""Returns the VBMeta arguments for partition.
|
||||||
|
|
||||||
|
It sets up the VBMeta argument by including the partition descriptor from the
|
||||||
|
given 'image', or by configuring the partition as a chained partition.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
partition: The name of the partition (e.g. "system").
|
||||||
|
image: The path to the partition image.
|
||||||
|
info_dict: A dict returned by common.LoadInfoDict(). Will use
|
||||||
|
OPTIONS.info_dict if None has been given.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of VBMeta arguments.
|
||||||
|
"""
|
||||||
|
if info_dict is None:
|
||||||
|
info_dict = OPTIONS.info_dict
|
||||||
|
|
||||||
|
# Check if chain partition is used.
|
||||||
|
key_path = info_dict.get("avb_" + partition + "_key_path")
|
||||||
|
if key_path:
|
||||||
|
chained_partition_arg = GetAvbChainedPartitionArg(partition, info_dict)
|
||||||
|
return ["--chain_partition", chained_partition_arg]
|
||||||
|
else:
|
||||||
|
return ["--include_descriptors_from_image", image]
|
||||||
|
|
||||||
|
|
||||||
def GetAvbChainedPartitionArg(partition, info_dict, key=None):
|
def GetAvbChainedPartitionArg(partition, info_dict, key=None):
|
||||||
"""Constructs and returns the arg to build or verify a chained partition.
|
"""Constructs and returns the arg to build or verify a chained partition.
|
||||||
|
|
||||||
@@ -647,6 +674,65 @@ def GetAvbChainedPartitionArg(partition, info_dict, key=None):
|
|||||||
return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path)
|
return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path)
|
||||||
|
|
||||||
|
|
||||||
|
def BuildVBMeta(image_path, partitions, name, needed_partitions):
|
||||||
|
"""Creates a VBMeta image.
|
||||||
|
|
||||||
|
It generates the requested VBMeta image. The requested image could be for
|
||||||
|
top-level or chained VBMeta image, which is determined based on the name.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
image_path: The output path for the new VBMeta image.
|
||||||
|
partitions: A dict that's keyed by partition names with image paths as
|
||||||
|
values. Only valid partition names are accepted, as listed in
|
||||||
|
common.AVB_PARTITIONS.
|
||||||
|
name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
|
||||||
|
needed_partitions: Partitions whose descriptors should be included into the
|
||||||
|
generated VBMeta image.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
AssertionError: On invalid input args.
|
||||||
|
"""
|
||||||
|
avbtool = OPTIONS.info_dict["avb_avbtool"]
|
||||||
|
cmd = [avbtool, "make_vbmeta_image", "--output", image_path]
|
||||||
|
AppendAVBSigningArgs(cmd, name)
|
||||||
|
|
||||||
|
for partition, path in partitions.items():
|
||||||
|
if partition not in needed_partitions:
|
||||||
|
continue
|
||||||
|
assert (partition in AVB_PARTITIONS or
|
||||||
|
partition in AVB_VBMETA_PARTITIONS), \
|
||||||
|
'Unknown partition: {}'.format(partition)
|
||||||
|
assert os.path.exists(path), \
|
||||||
|
'Failed to find {} for {}'.format(path, partition)
|
||||||
|
cmd.extend(GetAvbPartitionArg(partition, path))
|
||||||
|
|
||||||
|
args = OPTIONS.info_dict.get("avb_{}_args".format(name))
|
||||||
|
if args and args.strip():
|
||||||
|
split_args = shlex.split(args)
|
||||||
|
for index, arg in enumerate(split_args[:-1]):
|
||||||
|
# Sanity check that the image file exists. Some images might be defined
|
||||||
|
# as a path relative to source tree, which may not be available at the
|
||||||
|
# same location when running this script (we have the input target_files
|
||||||
|
# zip only). For such cases, we additionally scan other locations (e.g.
|
||||||
|
# IMAGES/, RADIO/, etc) before bailing out.
|
||||||
|
if arg == '--include_descriptors_from_image':
|
||||||
|
image_path = split_args[index + 1]
|
||||||
|
if os.path.exists(image_path):
|
||||||
|
continue
|
||||||
|
found = False
|
||||||
|
for dir_name in ['IMAGES', 'RADIO', 'PREBUILT_IMAGES']:
|
||||||
|
alt_path = os.path.join(
|
||||||
|
OPTIONS.input_tmp, dir_name, os.path.basename(image_path))
|
||||||
|
if os.path.exists(alt_path):
|
||||||
|
split_args[index + 1] = alt_path
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
assert found, 'Failed to find {}'.format(image_path)
|
||||||
|
cmd.extend(split_args)
|
||||||
|
|
||||||
|
RunAndCheckOutput(cmd)
|
||||||
|
|
||||||
|
|
||||||
def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
|
def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
|
||||||
has_ramdisk=False, two_step_image=False):
|
has_ramdisk=False, two_step_image=False):
|
||||||
"""Build a bootable image from the specified sourcedir.
|
"""Build a bootable image from the specified sourcedir.
|
||||||
|
@@ -24,9 +24,8 @@ build, the framework partial build should always be built with DAP enabled. The
|
|||||||
vendor partial build determines whether the merged result supports DAP.
|
vendor partial build determines whether the merged result supports DAP.
|
||||||
|
|
||||||
This script does not require builds to be built with 'make dist'.
|
This script does not require builds to be built with 'make dist'.
|
||||||
This script assumes that images other than super_empty.img do not require
|
This script regenerates super_empty.img and vbmeta.img if necessary. Other
|
||||||
regeneration, including vbmeta images.
|
images are assumed to not require regeneration.
|
||||||
TODO(b/137853921): Add support for regenerating vbmeta images.
|
|
||||||
|
|
||||||
Usage: merge_builds.py [args]
|
Usage: merge_builds.py [args]
|
||||||
|
|
||||||
@@ -39,6 +38,15 @@ Usage: merge_builds.py [args]
|
|||||||
|
|
||||||
--product_out_vendor product_out_vendor_path
|
--product_out_vendor product_out_vendor_path
|
||||||
Path to out/target/product/<vendor build>.
|
Path to out/target/product/<vendor build>.
|
||||||
|
|
||||||
|
--build_vbmeta
|
||||||
|
If provided, vbmeta.img will be regenerated in out/target/product/<vendor
|
||||||
|
build>.
|
||||||
|
|
||||||
|
--framework_misc_info_keys
|
||||||
|
The optional path to a newline-separated config file containing keys to
|
||||||
|
obtain from the framework instance of misc_info.txt, used for creating
|
||||||
|
vbmeta.img. The remaining keys come from the vendor instance.
|
||||||
"""
|
"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
@@ -55,6 +63,8 @@ OPTIONS = common.OPTIONS
|
|||||||
OPTIONS.framework_images = ("system",)
|
OPTIONS.framework_images = ("system",)
|
||||||
OPTIONS.product_out_framework = None
|
OPTIONS.product_out_framework = None
|
||||||
OPTIONS.product_out_vendor = None
|
OPTIONS.product_out_vendor = None
|
||||||
|
OPTIONS.build_vbmeta = False
|
||||||
|
OPTIONS.framework_misc_info_keys = None
|
||||||
|
|
||||||
|
|
||||||
def CreateImageSymlinks():
|
def CreateImageSymlinks():
|
||||||
@@ -82,6 +92,7 @@ def BuildSuperEmpty():
|
|||||||
# super_empty.img from the framework build.
|
# super_empty.img from the framework build.
|
||||||
if (framework_dict.get("use_dynamic_partitions") == "true") and (
|
if (framework_dict.get("use_dynamic_partitions") == "true") and (
|
||||||
vendor_dict.get("use_dynamic_partitions") == "true"):
|
vendor_dict.get("use_dynamic_partitions") == "true"):
|
||||||
|
logger.info("Building super_empty.img.")
|
||||||
merged_dict = dict(vendor_dict)
|
merged_dict = dict(vendor_dict)
|
||||||
merged_dict.update(
|
merged_dict.update(
|
||||||
common.MergeDynamicPartitionInfoDicts(
|
common.MergeDynamicPartitionInfoDicts(
|
||||||
@@ -96,10 +107,52 @@ def BuildSuperEmpty():
|
|||||||
build_super_image.BuildSuperImage(merged_dict, output_super_empty_path)
|
build_super_image.BuildSuperImage(merged_dict, output_super_empty_path)
|
||||||
|
|
||||||
|
|
||||||
|
def BuildVBMeta():
|
||||||
|
logger.info("Building vbmeta.img.")
|
||||||
|
|
||||||
|
framework_dict = common.LoadDictionaryFromFile(
|
||||||
|
os.path.join(OPTIONS.product_out_framework, "misc_info.txt"))
|
||||||
|
vendor_dict = common.LoadDictionaryFromFile(
|
||||||
|
os.path.join(OPTIONS.product_out_vendor, "misc_info.txt"))
|
||||||
|
merged_dict = dict(vendor_dict)
|
||||||
|
if OPTIONS.framework_misc_info_keys:
|
||||||
|
for key in common.LoadListFromFile(OPTIONS.framework_misc_info_keys):
|
||||||
|
merged_dict[key] = framework_dict[key]
|
||||||
|
|
||||||
|
# Build vbmeta.img using partitions in product_out_vendor.
|
||||||
|
partitions = {}
|
||||||
|
for partition in common.AVB_PARTITIONS:
|
||||||
|
partition_path = os.path.join(OPTIONS.product_out_vendor,
|
||||||
|
"%s.img" % partition)
|
||||||
|
if os.path.exists(partition_path):
|
||||||
|
partitions[partition] = partition_path
|
||||||
|
|
||||||
|
# vbmeta_partitions includes the partitions that should be included into
|
||||||
|
# top-level vbmeta.img, which are the ones that are not included in any
|
||||||
|
# chained VBMeta image plus the chained VBMeta images themselves.
|
||||||
|
vbmeta_partitions = common.AVB_PARTITIONS[:]
|
||||||
|
for partition in common.AVB_VBMETA_PARTITIONS:
|
||||||
|
chained_partitions = merged_dict.get("avb_%s" % partition, "").strip()
|
||||||
|
if chained_partitions:
|
||||||
|
partitions[partition] = os.path.join(OPTIONS.product_out_vendor,
|
||||||
|
"%s.img" % partition)
|
||||||
|
vbmeta_partitions = [
|
||||||
|
item for item in vbmeta_partitions
|
||||||
|
if item not in chained_partitions.split()
|
||||||
|
]
|
||||||
|
vbmeta_partitions.append(partition)
|
||||||
|
|
||||||
|
output_vbmeta_path = os.path.join(OPTIONS.product_out_vendor, "vbmeta.img")
|
||||||
|
OPTIONS.info_dict = merged_dict
|
||||||
|
common.BuildVBMeta(output_vbmeta_path, partitions, "vbmeta",
|
||||||
|
vbmeta_partitions)
|
||||||
|
|
||||||
|
|
||||||
def MergeBuilds():
|
def MergeBuilds():
|
||||||
CreateImageSymlinks()
|
CreateImageSymlinks()
|
||||||
BuildSuperEmpty()
|
BuildSuperEmpty()
|
||||||
# TODO(b/137853921): Add support for regenerating vbmeta images.
|
if OPTIONS.build_vbmeta:
|
||||||
|
BuildVBMeta()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -112,6 +165,10 @@ def main():
|
|||||||
OPTIONS.product_out_framework = a
|
OPTIONS.product_out_framework = a
|
||||||
elif o == "--product_out_vendor":
|
elif o == "--product_out_vendor":
|
||||||
OPTIONS.product_out_vendor = a
|
OPTIONS.product_out_vendor = a
|
||||||
|
elif o == "--build_vbmeta":
|
||||||
|
OPTIONS.build_vbmeta = True
|
||||||
|
elif o == "--framework_misc_info_keys":
|
||||||
|
OPTIONS.framework_misc_info_keys = a
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -123,6 +180,8 @@ def main():
|
|||||||
"framework_images=",
|
"framework_images=",
|
||||||
"product_out_framework=",
|
"product_out_framework=",
|
||||||
"product_out_vendor=",
|
"product_out_vendor=",
|
||||||
|
"build_vbmeta",
|
||||||
|
"framework_misc_info_keys=",
|
||||||
],
|
],
|
||||||
extra_option_handler=option_handler)
|
extra_option_handler=option_handler)
|
||||||
|
|
||||||
|
@@ -21,7 +21,7 @@ import zipfile
|
|||||||
import common
|
import common
|
||||||
import test_utils
|
import test_utils
|
||||||
from add_img_to_target_files import (
|
from add_img_to_target_files import (
|
||||||
AddCareMapForAbOta, AddPackRadioImages, AppendVBMetaArgsForPartition,
|
AddCareMapForAbOta, AddPackRadioImages,
|
||||||
CheckAbOtaImages, GetCareMap)
|
CheckAbOtaImages, GetCareMap)
|
||||||
from rangelib import RangeSet
|
from rangelib import RangeSet
|
||||||
|
|
||||||
@@ -379,32 +379,6 @@ class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase):
|
|||||||
# The existing entry should be scheduled to be replaced.
|
# The existing entry should be scheduled to be replaced.
|
||||||
self.assertIn('META/care_map.pb', OPTIONS.replace_updated_files_list)
|
self.assertIn('META/care_map.pb', OPTIONS.replace_updated_files_list)
|
||||||
|
|
||||||
def test_AppendVBMetaArgsForPartition(self):
|
|
||||||
OPTIONS.info_dict = {}
|
|
||||||
cmd = []
|
|
||||||
AppendVBMetaArgsForPartition(cmd, 'system', '/path/to/system.img')
|
|
||||||
self.assertEqual(
|
|
||||||
['--include_descriptors_from_image', '/path/to/system.img'], cmd)
|
|
||||||
|
|
||||||
@test_utils.SkipIfExternalToolsUnavailable()
|
|
||||||
def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
|
|
||||||
testdata_dir = test_utils.get_testdata_dir()
|
|
||||||
pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
|
|
||||||
OPTIONS.info_dict = {
|
|
||||||
'avb_avbtool': 'avbtool',
|
|
||||||
'avb_vendor_key_path': pubkey,
|
|
||||||
'avb_vendor_rollback_index_location': 5,
|
|
||||||
}
|
|
||||||
cmd = []
|
|
||||||
AppendVBMetaArgsForPartition(cmd, 'vendor', '/path/to/vendor.img')
|
|
||||||
self.assertEqual(2, len(cmd))
|
|
||||||
self.assertEqual('--chain_partition', cmd[0])
|
|
||||||
chained_partition_args = cmd[1].split(':')
|
|
||||||
self.assertEqual(3, len(chained_partition_args))
|
|
||||||
self.assertEqual('vendor', chained_partition_args[0])
|
|
||||||
self.assertEqual('5', chained_partition_args[1])
|
|
||||||
self.assertTrue(os.path.exists(chained_partition_args[2]))
|
|
||||||
|
|
||||||
def test_GetCareMap(self):
|
def test_GetCareMap(self):
|
||||||
sparse_image = test_utils.construct_sparse_image([
|
sparse_image = test_utils.construct_sparse_image([
|
||||||
(0xCAC1, 6),
|
(0xCAC1, 6),
|
||||||
|
@@ -1137,6 +1137,30 @@ class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
|
|||||||
}
|
}
|
||||||
self.assertEqual(merged_dict, expected_merged_dict)
|
self.assertEqual(merged_dict, expected_merged_dict)
|
||||||
|
|
||||||
|
def test_GetAvbPartitionArg(self):
|
||||||
|
info_dict = {}
|
||||||
|
cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
|
||||||
|
self.assertEqual(
|
||||||
|
['--include_descriptors_from_image', '/path/to/system.img'], cmd)
|
||||||
|
|
||||||
|
@test_utils.SkipIfExternalToolsUnavailable()
|
||||||
|
def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
|
||||||
|
testdata_dir = test_utils.get_testdata_dir()
|
||||||
|
pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
|
||||||
|
info_dict = {
|
||||||
|
'avb_avbtool': 'avbtool',
|
||||||
|
'avb_vendor_key_path': pubkey,
|
||||||
|
'avb_vendor_rollback_index_location': 5,
|
||||||
|
}
|
||||||
|
cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
|
||||||
|
self.assertEqual(2, len(cmd))
|
||||||
|
self.assertEqual('--chain_partition', cmd[0])
|
||||||
|
chained_partition_args = cmd[1].split(':')
|
||||||
|
self.assertEqual(3, len(chained_partition_args))
|
||||||
|
self.assertEqual('vendor', chained_partition_args[0])
|
||||||
|
self.assertEqual('5', chained_partition_args[1])
|
||||||
|
self.assertTrue(os.path.exists(chained_partition_args[2]))
|
||||||
|
|
||||||
|
|
||||||
class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
|
class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
|
||||||
"""Checks the format of install-recovery.sh.
|
"""Checks the format of install-recovery.sh.
|
||||||
|
Reference in New Issue
Block a user