| #!/usr/bin/env python |
| # |
| # Copyright (C) 2019 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| # |
| """Merges two non-dist partial builds together. |
| |
| Given two partial builds, a framework build and a vendor build, merge the builds |
| together so that the images can be flashed using 'fastboot flashall'. |
| |
| To support both DAP and non-DAP vendor builds with a single framework partial |
| build, the framework partial build should always be built with DAP enabled. The |
| vendor partial build determines whether the merged result supports DAP. |
| |
| This script does not require builds to be built with 'make dist'. |
| This script regenerates super_empty.img and vbmeta.img if necessary. Other |
| images are assumed to not require regeneration. |
| |
| Usage: merge_builds.py [args] |
| |
| --framework_images comma_separated_image_list |
| Comma-separated list of image names that should come from the framework |
| build. |
| |
| --product_out_framework product_out_framework_path |
| Path to out/target/product/<framework build>. |
| |
| --product_out_vendor product_out_vendor_path |
| 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. |
| |
| --avb_resolve_rollback_index_location_conflict |
| If provided, resolve the conflict AVB rollback index location when |
| necessary. |
| """ |
| from __future__ import print_function |
| |
| import logging |
| import os |
| import sys |
| |
| import build_super_image |
| import common |
| |
| logger = logging.getLogger(__name__) |
| |
| OPTIONS = common.OPTIONS |
| OPTIONS.framework_images = ("system",) |
| OPTIONS.product_out_framework = None |
| OPTIONS.product_out_vendor = None |
| OPTIONS.build_vbmeta = False |
| OPTIONS.framework_misc_info_keys = None |
| OPTIONS.avb_resolve_rollback_index_location_conflict = False |
| |
| |
| def CreateImageSymlinks(): |
| for image in OPTIONS.framework_images: |
| image_path = os.path.join(OPTIONS.product_out_framework, "%s.img" % image) |
| symlink_path = os.path.join(OPTIONS.product_out_vendor, "%s.img" % image) |
| if os.path.exists(symlink_path): |
| if os.path.islink(symlink_path): |
| os.remove(symlink_path) |
| else: |
| raise ValueError("Attempting to overwrite built image: %s" % |
| symlink_path) |
| os.symlink(image_path, symlink_path) |
| |
| |
| def BuildSuperEmpty(): |
| 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")) |
| # Regenerate super_empty.img if both partial builds enable DAP. If only the |
| # the vendor build enables DAP, the vendor build's existing super_empty.img |
| # will be reused. If only the framework build should enable DAP, super_empty |
| # should be included in the --framework_images flag to copy the existing |
| # super_empty.img from the framework build. |
| if (framework_dict.get("use_dynamic_partitions") == "true") and ( |
| vendor_dict.get("use_dynamic_partitions") == "true"): |
| logger.info("Building super_empty.img.") |
| merged_dict = dict(vendor_dict) |
| merged_dict.update( |
| common.MergeDynamicPartitionInfoDicts( |
| framework_dict=framework_dict, vendor_dict=vendor_dict)) |
| output_super_empty_path = os.path.join(OPTIONS.product_out_vendor, |
| "super_empty.img") |
| 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, |
| OPTIONS.avb_resolve_rollback_index_location_conflict) |
| |
| |
| def MergeBuilds(): |
| CreateImageSymlinks() |
| BuildSuperEmpty() |
| if OPTIONS.build_vbmeta: |
| BuildVBMeta() |
| |
| |
| def main(): |
| common.InitLogging() |
| |
| def option_handler(o, a): |
| if o == "--framework_images": |
| OPTIONS.framework_images = [i.strip() for i in a.split(",")] |
| elif o == "--product_out_framework": |
| OPTIONS.product_out_framework = a |
| elif o == "--product_out_vendor": |
| 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 |
| elif o == "--avb_resolve_rollback_index_location_conflict": |
| OPTIONS.avb_resolve_rollback_index_location_conflict = True |
| else: |
| return False |
| return True |
| |
| args = common.ParseOptions( |
| sys.argv[1:], |
| __doc__, |
| extra_long_opts=[ |
| "framework_images=", |
| "product_out_framework=", |
| "product_out_vendor=", |
| "build_vbmeta", |
| "framework_misc_info_keys=", |
| "avb_resolve_rollback_index_location_conflict" |
| ], |
| extra_option_handler=option_handler) |
| |
| if (args or OPTIONS.product_out_framework is None or |
| OPTIONS.product_out_vendor is None): |
| common.Usage(__doc__) |
| sys.exit(1) |
| |
| MergeBuilds() |
| |
| |
| if __name__ == "__main__": |
| main() |