Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # |
| 3 | # Copyright (C) 2019 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| 6 | # use this file except in compliance with the License. You may obtain a copy of |
| 7 | # the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| 14 | # License for the specific language governing permissions and limitations under |
| 15 | # the License. |
| 16 | # |
| 17 | """Merges two non-dist partial builds together. |
| 18 | |
| 19 | Given two partial builds, a framework build and a vendor build, merge the builds |
| 20 | together so that the images can be flashed using 'fastboot flashall'. |
| 21 | |
| 22 | To support both DAP and non-DAP vendor builds with a single framework partial |
| 23 | build, the framework partial build should always be built with DAP enabled. The |
| 24 | vendor partial build determines whether the merged result supports DAP. |
| 25 | |
| 26 | This script does not require builds to be built with 'make dist'. |
Daniel Norman | 276f062 | 2019-07-26 14:13:51 -0700 | [diff] [blame^] | 27 | This script regenerates super_empty.img and vbmeta.img if necessary. Other |
| 28 | images are assumed to not require regeneration. |
Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 29 | |
| 30 | Usage: merge_builds.py [args] |
| 31 | |
| 32 | --framework_images comma_separated_image_list |
| 33 | Comma-separated list of image names that should come from the framework |
| 34 | build. |
| 35 | |
| 36 | --product_out_framework product_out_framework_path |
| 37 | Path to out/target/product/<framework build>. |
| 38 | |
| 39 | --product_out_vendor product_out_vendor_path |
| 40 | Path to out/target/product/<vendor build>. |
Daniel Norman | 276f062 | 2019-07-26 14:13:51 -0700 | [diff] [blame^] | 41 | |
| 42 | --build_vbmeta |
| 43 | If provided, vbmeta.img will be regenerated in out/target/product/<vendor |
| 44 | build>. |
| 45 | |
| 46 | --framework_misc_info_keys |
| 47 | The optional path to a newline-separated config file containing keys to |
| 48 | obtain from the framework instance of misc_info.txt, used for creating |
| 49 | vbmeta.img. The remaining keys come from the vendor instance. |
Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 50 | """ |
| 51 | from __future__ import print_function |
| 52 | |
| 53 | import logging |
| 54 | import os |
| 55 | import sys |
| 56 | |
| 57 | import build_super_image |
| 58 | import common |
| 59 | |
| 60 | logger = logging.getLogger(__name__) |
| 61 | |
| 62 | OPTIONS = common.OPTIONS |
| 63 | OPTIONS.framework_images = ("system",) |
| 64 | OPTIONS.product_out_framework = None |
| 65 | OPTIONS.product_out_vendor = None |
Daniel Norman | 276f062 | 2019-07-26 14:13:51 -0700 | [diff] [blame^] | 66 | OPTIONS.build_vbmeta = False |
| 67 | OPTIONS.framework_misc_info_keys = None |
Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 68 | |
| 69 | |
| 70 | def CreateImageSymlinks(): |
| 71 | for image in OPTIONS.framework_images: |
| 72 | image_path = os.path.join(OPTIONS.product_out_framework, "%s.img" % image) |
| 73 | symlink_path = os.path.join(OPTIONS.product_out_vendor, "%s.img" % image) |
| 74 | if os.path.exists(symlink_path): |
| 75 | if os.path.islink(symlink_path): |
| 76 | os.remove(symlink_path) |
| 77 | else: |
| 78 | raise ValueError("Attempting to overwrite built image: %s" % |
| 79 | symlink_path) |
| 80 | os.symlink(image_path, symlink_path) |
| 81 | |
| 82 | |
| 83 | def BuildSuperEmpty(): |
| 84 | framework_dict = common.LoadDictionaryFromFile( |
| 85 | os.path.join(OPTIONS.product_out_framework, "misc_info.txt")) |
| 86 | vendor_dict = common.LoadDictionaryFromFile( |
| 87 | os.path.join(OPTIONS.product_out_vendor, "misc_info.txt")) |
| 88 | # Regenerate super_empty.img if both partial builds enable DAP. If only the |
| 89 | # the vendor build enables DAP, the vendor build's existing super_empty.img |
| 90 | # will be reused. If only the framework build should enable DAP, super_empty |
| 91 | # should be included in the --framework_images flag to copy the existing |
| 92 | # super_empty.img from the framework build. |
| 93 | if (framework_dict.get("use_dynamic_partitions") == "true") and ( |
| 94 | vendor_dict.get("use_dynamic_partitions") == "true"): |
Daniel Norman | 276f062 | 2019-07-26 14:13:51 -0700 | [diff] [blame^] | 95 | logger.info("Building super_empty.img.") |
Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 96 | merged_dict = dict(vendor_dict) |
| 97 | merged_dict.update( |
| 98 | common.MergeDynamicPartitionInfoDicts( |
| 99 | framework_dict=framework_dict, |
| 100 | vendor_dict=vendor_dict, |
| 101 | size_prefix="super_", |
| 102 | size_suffix="_group_size", |
| 103 | list_prefix="super_", |
| 104 | list_suffix="_partition_list")) |
| 105 | output_super_empty_path = os.path.join(OPTIONS.product_out_vendor, |
| 106 | "super_empty.img") |
| 107 | build_super_image.BuildSuperImage(merged_dict, output_super_empty_path) |
| 108 | |
| 109 | |
Daniel Norman | 276f062 | 2019-07-26 14:13:51 -0700 | [diff] [blame^] | 110 | def BuildVBMeta(): |
| 111 | logger.info("Building vbmeta.img.") |
| 112 | |
| 113 | framework_dict = common.LoadDictionaryFromFile( |
| 114 | os.path.join(OPTIONS.product_out_framework, "misc_info.txt")) |
| 115 | vendor_dict = common.LoadDictionaryFromFile( |
| 116 | os.path.join(OPTIONS.product_out_vendor, "misc_info.txt")) |
| 117 | merged_dict = dict(vendor_dict) |
| 118 | if OPTIONS.framework_misc_info_keys: |
| 119 | for key in common.LoadListFromFile(OPTIONS.framework_misc_info_keys): |
| 120 | merged_dict[key] = framework_dict[key] |
| 121 | |
| 122 | # Build vbmeta.img using partitions in product_out_vendor. |
| 123 | partitions = {} |
| 124 | for partition in common.AVB_PARTITIONS: |
| 125 | partition_path = os.path.join(OPTIONS.product_out_vendor, |
| 126 | "%s.img" % partition) |
| 127 | if os.path.exists(partition_path): |
| 128 | partitions[partition] = partition_path |
| 129 | |
| 130 | # vbmeta_partitions includes the partitions that should be included into |
| 131 | # top-level vbmeta.img, which are the ones that are not included in any |
| 132 | # chained VBMeta image plus the chained VBMeta images themselves. |
| 133 | vbmeta_partitions = common.AVB_PARTITIONS[:] |
| 134 | for partition in common.AVB_VBMETA_PARTITIONS: |
| 135 | chained_partitions = merged_dict.get("avb_%s" % partition, "").strip() |
| 136 | if chained_partitions: |
| 137 | partitions[partition] = os.path.join(OPTIONS.product_out_vendor, |
| 138 | "%s.img" % partition) |
| 139 | vbmeta_partitions = [ |
| 140 | item for item in vbmeta_partitions |
| 141 | if item not in chained_partitions.split() |
| 142 | ] |
| 143 | vbmeta_partitions.append(partition) |
| 144 | |
| 145 | output_vbmeta_path = os.path.join(OPTIONS.product_out_vendor, "vbmeta.img") |
| 146 | OPTIONS.info_dict = merged_dict |
| 147 | common.BuildVBMeta(output_vbmeta_path, partitions, "vbmeta", |
| 148 | vbmeta_partitions) |
| 149 | |
| 150 | |
Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 151 | def MergeBuilds(): |
| 152 | CreateImageSymlinks() |
| 153 | BuildSuperEmpty() |
Daniel Norman | 276f062 | 2019-07-26 14:13:51 -0700 | [diff] [blame^] | 154 | if OPTIONS.build_vbmeta: |
| 155 | BuildVBMeta() |
Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 156 | |
| 157 | |
| 158 | def main(): |
| 159 | common.InitLogging() |
| 160 | |
| 161 | def option_handler(o, a): |
| 162 | if o == "--framework_images": |
| 163 | OPTIONS.framework_images = [i.strip() for i in a.split(",")] |
| 164 | elif o == "--product_out_framework": |
| 165 | OPTIONS.product_out_framework = a |
| 166 | elif o == "--product_out_vendor": |
| 167 | OPTIONS.product_out_vendor = a |
Daniel Norman | 276f062 | 2019-07-26 14:13:51 -0700 | [diff] [blame^] | 168 | elif o == "--build_vbmeta": |
| 169 | OPTIONS.build_vbmeta = True |
| 170 | elif o == "--framework_misc_info_keys": |
| 171 | OPTIONS.framework_misc_info_keys = a |
Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 172 | else: |
| 173 | return False |
| 174 | return True |
| 175 | |
| 176 | args = common.ParseOptions( |
| 177 | sys.argv[1:], |
| 178 | __doc__, |
| 179 | extra_long_opts=[ |
| 180 | "framework_images=", |
| 181 | "product_out_framework=", |
| 182 | "product_out_vendor=", |
Daniel Norman | 276f062 | 2019-07-26 14:13:51 -0700 | [diff] [blame^] | 183 | "build_vbmeta", |
| 184 | "framework_misc_info_keys=", |
Daniel Norman | bfc51ef | 2019-07-24 14:34:54 -0700 | [diff] [blame] | 185 | ], |
| 186 | extra_option_handler=option_handler) |
| 187 | |
| 188 | if (args or OPTIONS.product_out_framework is None or |
| 189 | OPTIONS.product_out_vendor is None): |
| 190 | common.Usage(__doc__) |
| 191 | sys.exit(1) |
| 192 | |
| 193 | MergeBuilds() |
| 194 | |
| 195 | |
| 196 | if __name__ == "__main__": |
| 197 | main() |