Loading tools/releasetools/common.py +48 −10 Original line number Diff line number Diff line Loading @@ -2524,11 +2524,12 @@ class BlockDifference(object): self.device = 'map_partition("%s")' % partition else: if OPTIONS.source_info_dict is None: _, device_path = GetTypeAndDevice("/" + partition, OPTIONS.info_dict) _, device_expr = GetTypeAndDeviceExpr("/" + partition, OPTIONS.info_dict) else: _, device_path = GetTypeAndDevice("/" + partition, _, device_expr = GetTypeAndDeviceExpr("/" + partition, OPTIONS.source_info_dict) self.device = '"%s"' % device_path self.device = device_expr @property def required_cache(self): Loading Loading @@ -2760,16 +2761,51 @@ PARTITION_TYPES = { "squashfs": "EMMC" } def GetTypeAndDevice(mount_point, info): def GetTypeAndDevice(mount_point, info, check_no_slot=True): """ Use GetTypeAndDeviceExpr whenever possible. This function is kept for backwards compatibility. It aborts if the fstab entry has slotselect option (unless check_no_slot is explicitly set to False). """ fstab = info["fstab"] if fstab: if check_no_slot: assert not fstab[mount_point].slotselect, \ "Use GetTypeAndDeviceExpr instead" return (PARTITION_TYPES[fstab[mount_point].fs_type], fstab[mount_point].device) else: raise KeyError def GetTypeAndDeviceExpr(mount_point, info): """ Return the filesystem of the partition, and an edify expression that evaluates to the device at runtime. """ fstab = info["fstab"] if fstab: p = fstab[mount_point] device_expr = '"%s"' % fstab[mount_point].device if p.slotselect: device_expr = 'add_slot_suffix(%s)' % device_expr return (PARTITION_TYPES[fstab[mount_point].fs_type], device_expr) else: raise KeyError def GetEntryForDevice(fstab, device): """ Returns: The first entry in fstab whose device is the given value. """ if not fstab: return None for mount_point in fstab: if fstab[mount_point].device == device: return fstab[mount_point] return None def ParseCertificate(data): """Parses and converts a PEM-encoded certificate into DER-encoded. Loading Loading @@ -2894,8 +2930,10 @@ def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img, try: # The following GetTypeAndDevice()s need to use the path in the target # info_dict instead of source_info_dict. boot_type, boot_device = GetTypeAndDevice("/boot", info_dict) recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict) boot_type, boot_device = GetTypeAndDevice("/boot", info_dict, check_no_slot=False) recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict, check_no_slot=False) except KeyError: return Loading Loading @@ -2937,8 +2975,8 @@ fi 'recovery_size': recovery_img.size, 'recovery_sha1': recovery_img.sha1, 'boot_type': boot_type, 'boot_device': boot_device, 'recovery_type': recovery_type, 'boot_device': boot_device + '$(getprop ro.boot.slot_suffix)', 'recovery_type': recovery_type + '$(getprop ro.boot.slot_suffix)', 'recovery_device': recovery_device, 'bonus_args': bonus_args} Loading tools/releasetools/edify_generator.py +98 −20 Original line number Diff line number Diff line Loading @@ -183,11 +183,30 @@ class EdifyGenerator(object): It checks the checksums of the given partitions. If none of them matches the expected checksum, updater will additionally look for a backup on /cache. """ self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExprCheck") self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExprCheck") self.PatchPartitionExprCheck('"%s"' % target, '"%s"' % source) def PatchPartitionExprCheck(self, target_expr, source_expr): """Checks whether updater can patch the given partitions. It checks the checksums of the given partitions. If none of them matches the expected checksum, updater will additionally look for a backup on /cache. Args: target_expr: an Edify expression that serves as the target arg to patch_partition. Must be evaluated to a string in the form of foo:bar:baz:quux source_expr: an Edify expression that serves as the source arg to patch_partition. Must be evaluated to a string in the form of foo:bar:baz:quux """ self.script.append(self.WordWrap(( 'patch_partition_check("{target}",\0"{source}") ||\n abort(' '"E{code}: \\"{target}\\" or \\"{source}\\" has unexpected ' 'contents.");').format( target=target, source=source, 'patch_partition_check({target},\0{source}) ||\n abort(' 'concat("E{code}: \\"",{target},"\\" or \\"",{source},"\\" has ' 'unexpected contents."));').format( target=target_expr, source=source_expr, code=common.ErrorCode.BAD_PATCH_FILE))) def CacheFreeSpaceCheck(self, amount): Loading Loading @@ -218,8 +237,9 @@ class EdifyGenerator(object): mount_flags = mount_dict.get(p.fs_type, "") if p.context is not None: mount_flags = p.context + ("," + mount_flags if mount_flags else "") self.script.append('mount("%s", "%s", "%s", "%s", "%s");' % ( p.fs_type, common.PARTITION_TYPES[p.fs_type], p.device, self.script.append('mount("%s", "%s", %s, "%s", "%s");' % ( p.fs_type, common.PARTITION_TYPES[p.fs_type], self._GetSlotSuffixDeviceForEntry(p), p.mount_point, mount_flags)) self.mounts.add(p.mount_point) Loading @@ -242,8 +262,9 @@ class EdifyGenerator(object): raise ValueError("Partition %s cannot be tuned\n" % (partition,)) self.script.append( 'tune2fs(' + "".join(['"%s", ' % (i,) for i in options]) + '"%s") || abort("E%d: Failed to tune partition %s");' % ( p.device, common.ErrorCode.TUNE_PARTITION_FAILURE, partition)) '%s) || abort("E%d: Failed to tune partition %s");' % ( self._GetSlotSuffixDeviceForEntry(p), common.ErrorCode.TUNE_PARTITION_FAILURE, partition)) def FormatPartition(self, partition): """Format the given partition, specified by its mount point (eg, Loading @@ -252,18 +273,19 @@ class EdifyGenerator(object): fstab = self.fstab if fstab: p = fstab[partition] self.script.append('format("%s", "%s", "%s", "%s", "%s");' % self.script.append('format("%s", "%s", %s, "%s", "%s");' % (p.fs_type, common.PARTITION_TYPES[p.fs_type], p.device, p.length, p.mount_point)) self._GetSlotSuffixDeviceForEntry(p), p.length, p.mount_point)) def WipeBlockDevice(self, partition): if partition not in ("/system", "/vendor"): raise ValueError(("WipeBlockDevice doesn't work on %s\n") % (partition,)) fstab = self.fstab size = self.info.get(partition.lstrip("/") + "_size", None) device = fstab[partition].device device = self._GetSlotSuffixDeviceForEntry(fstab[partition]) self.script.append('wipe_block_device("%s", %s);' % (device, size)) self.script.append('wipe_block_device(%s, %s);' % (device, size)) def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs): """Apply binary patches (in *patchpairs) to the given srcfile to Loading Loading @@ -296,14 +318,69 @@ class EdifyGenerator(object): self.PatchPartition(target, source, patch) def PatchPartition(self, target, source, patch): """Applies the patch to the source partition and writes it to target.""" """ Applies the patch to the source partition and writes it to target. Args: target: the target arg to patch_partition. Must be in the form of foo:bar:baz:quux source: the source arg to patch_partition. Must be in the form of foo:bar:baz:quux patch: the patch arg to patch_partition. Must be an unquoted string. """ self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExpr") self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExpr") self.PatchPartitionExpr('"%s"' % target, '"%s"' % source, '"%s"' % patch) def PatchPartitionExpr(self, target_expr, source_expr, patch_expr): """ Applies the patch to the source partition and writes it to target. Args: target_expr: an Edify expression that serves as the target arg to patch_partition. Must be evaluated to a string in the form of foo:bar:baz:quux source_expr: an Edify expression that serves as the source arg to patch_partition. Must be evaluated to a string in the form of foo:bar:baz:quux patch_expr: an Edify expression that serves as the patch arg to patch_partition. Must be evaluated to a string. """ self.script.append(self.WordWrap(( 'patch_partition("{target}",\0"{source}",\0' 'package_extract_file("{patch}")) ||\n' ' abort("E{code}: Failed to apply patch to {source}");').format( target=target, source=source, patch=patch, 'patch_partition({target},\0{source},\0' 'package_extract_file({patch})) ||\n' ' abort(concat(' ' "E{code}: Failed to apply patch to ",{source}));').format( target=target_expr, source=source_expr, patch=patch_expr, code=common.ErrorCode.APPLY_PATCH_FAILURE))) def _GetSlotSuffixDeviceForEntry(self, entry=None): """ Args: entry: the fstab entry of device "foo" Returns: An edify expression. Caller must not quote result. If foo is slot suffixed, it returns 'add_slot_suffix("foo")' Otherwise it returns '"foo"' (quoted) """ assert entry is not None if entry.slotselect: return 'add_slot_suffix("%s")' % entry.device return '"%s"' % entry.device def _CheckSecondTokenNotSlotSuffixed(self, s, fn): lst = s.split(':') assert(len(s) == 4), "{} does not contain 4 tokens".format(s) if self.fstab: entry = common.GetEntryForDevice(s[1]) if entry is not None: assert not entry.slotselect, \ "Use %s because %s is slot suffixed" % (fn, s[1]) def WriteRawImage(self, mount_point, fn, mapfn=None): """Write the given package file into the partition for the given mount point.""" Loading @@ -312,15 +389,16 @@ class EdifyGenerator(object): if fstab: p = fstab[mount_point] partition_type = common.PARTITION_TYPES[p.fs_type] args = {'device': p.device, 'fn': fn} device = self._GetSlotSuffixDeviceForEntry(p) args = {'device': device, 'fn': fn} if partition_type == "EMMC": if mapfn: args["map"] = mapfn self.script.append( 'package_extract_file("%(fn)s", "%(device)s", "%(map)s");' % args) 'package_extract_file("%(fn)s", %(device)s, "%(map)s");' % args) else: self.script.append( 'package_extract_file("%(fn)s", "%(device)s");' % args) 'package_extract_file("%(fn)s", %(device)s);' % args) else: raise ValueError( "don't know how to write \"%s\" partitions" % p.fs_type) Loading tools/releasetools/ota_from_target_files.py +12 −12 Original line number Diff line number Diff line Loading @@ -1458,7 +1458,8 @@ else if get_stage("%(bcb_dev)s") != "3/3" then required_cache_sizes = [diff.required_cache for diff in block_diff_dict.values()] if updating_boot: boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info) boot_type, boot_device_expr = common.GetTypeAndDeviceExpr("/boot", source_info) d = common.Difference(target_boot, source_boot) _, _, d = d.ComputePatch() if d is None: Loading @@ -1473,11 +1474,11 @@ else if get_stage("%(bcb_dev)s") != "3/3" then common.ZipWriteStr(output_zip, "boot.img.p", d) script.PatchPartitionCheck( "{}:{}:{}:{}".format( boot_type, boot_device, target_boot.size, target_boot.sha1), "{}:{}:{}:{}".format( boot_type, boot_device, source_boot.size, source_boot.sha1)) target_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, target_boot.size, target_boot.sha1) source_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, source_boot.size, source_boot.sha1) script.PatchPartitionExprCheck(target_expr, source_expr) required_cache_sizes.append(target_boot.size) Loading Loading @@ -1545,12 +1546,11 @@ else logger.info("boot image changed; including patch.") script.Print("Patching boot image...") script.ShowProgress(0.1, 10) script.PatchPartition( '{}:{}:{}:{}'.format( boot_type, boot_device, target_boot.size, target_boot.sha1), '{}:{}:{}:{}'.format( boot_type, boot_device, source_boot.size, source_boot.sha1), 'boot.img.p') target_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, target_boot.size, target_boot.sha1) source_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, source_boot.size, source_boot.sha1) script.PatchPartitionExpr(target_expr, source_expr, '"boot.img.p"') else: logger.info("boot image unchanged; skipping.") Loading Loading
tools/releasetools/common.py +48 −10 Original line number Diff line number Diff line Loading @@ -2524,11 +2524,12 @@ class BlockDifference(object): self.device = 'map_partition("%s")' % partition else: if OPTIONS.source_info_dict is None: _, device_path = GetTypeAndDevice("/" + partition, OPTIONS.info_dict) _, device_expr = GetTypeAndDeviceExpr("/" + partition, OPTIONS.info_dict) else: _, device_path = GetTypeAndDevice("/" + partition, _, device_expr = GetTypeAndDeviceExpr("/" + partition, OPTIONS.source_info_dict) self.device = '"%s"' % device_path self.device = device_expr @property def required_cache(self): Loading Loading @@ -2760,16 +2761,51 @@ PARTITION_TYPES = { "squashfs": "EMMC" } def GetTypeAndDevice(mount_point, info): def GetTypeAndDevice(mount_point, info, check_no_slot=True): """ Use GetTypeAndDeviceExpr whenever possible. This function is kept for backwards compatibility. It aborts if the fstab entry has slotselect option (unless check_no_slot is explicitly set to False). """ fstab = info["fstab"] if fstab: if check_no_slot: assert not fstab[mount_point].slotselect, \ "Use GetTypeAndDeviceExpr instead" return (PARTITION_TYPES[fstab[mount_point].fs_type], fstab[mount_point].device) else: raise KeyError def GetTypeAndDeviceExpr(mount_point, info): """ Return the filesystem of the partition, and an edify expression that evaluates to the device at runtime. """ fstab = info["fstab"] if fstab: p = fstab[mount_point] device_expr = '"%s"' % fstab[mount_point].device if p.slotselect: device_expr = 'add_slot_suffix(%s)' % device_expr return (PARTITION_TYPES[fstab[mount_point].fs_type], device_expr) else: raise KeyError def GetEntryForDevice(fstab, device): """ Returns: The first entry in fstab whose device is the given value. """ if not fstab: return None for mount_point in fstab: if fstab[mount_point].device == device: return fstab[mount_point] return None def ParseCertificate(data): """Parses and converts a PEM-encoded certificate into DER-encoded. Loading Loading @@ -2894,8 +2930,10 @@ def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img, try: # The following GetTypeAndDevice()s need to use the path in the target # info_dict instead of source_info_dict. boot_type, boot_device = GetTypeAndDevice("/boot", info_dict) recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict) boot_type, boot_device = GetTypeAndDevice("/boot", info_dict, check_no_slot=False) recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict, check_no_slot=False) except KeyError: return Loading Loading @@ -2937,8 +2975,8 @@ fi 'recovery_size': recovery_img.size, 'recovery_sha1': recovery_img.sha1, 'boot_type': boot_type, 'boot_device': boot_device, 'recovery_type': recovery_type, 'boot_device': boot_device + '$(getprop ro.boot.slot_suffix)', 'recovery_type': recovery_type + '$(getprop ro.boot.slot_suffix)', 'recovery_device': recovery_device, 'bonus_args': bonus_args} Loading
tools/releasetools/edify_generator.py +98 −20 Original line number Diff line number Diff line Loading @@ -183,11 +183,30 @@ class EdifyGenerator(object): It checks the checksums of the given partitions. If none of them matches the expected checksum, updater will additionally look for a backup on /cache. """ self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExprCheck") self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExprCheck") self.PatchPartitionExprCheck('"%s"' % target, '"%s"' % source) def PatchPartitionExprCheck(self, target_expr, source_expr): """Checks whether updater can patch the given partitions. It checks the checksums of the given partitions. If none of them matches the expected checksum, updater will additionally look for a backup on /cache. Args: target_expr: an Edify expression that serves as the target arg to patch_partition. Must be evaluated to a string in the form of foo:bar:baz:quux source_expr: an Edify expression that serves as the source arg to patch_partition. Must be evaluated to a string in the form of foo:bar:baz:quux """ self.script.append(self.WordWrap(( 'patch_partition_check("{target}",\0"{source}") ||\n abort(' '"E{code}: \\"{target}\\" or \\"{source}\\" has unexpected ' 'contents.");').format( target=target, source=source, 'patch_partition_check({target},\0{source}) ||\n abort(' 'concat("E{code}: \\"",{target},"\\" or \\"",{source},"\\" has ' 'unexpected contents."));').format( target=target_expr, source=source_expr, code=common.ErrorCode.BAD_PATCH_FILE))) def CacheFreeSpaceCheck(self, amount): Loading Loading @@ -218,8 +237,9 @@ class EdifyGenerator(object): mount_flags = mount_dict.get(p.fs_type, "") if p.context is not None: mount_flags = p.context + ("," + mount_flags if mount_flags else "") self.script.append('mount("%s", "%s", "%s", "%s", "%s");' % ( p.fs_type, common.PARTITION_TYPES[p.fs_type], p.device, self.script.append('mount("%s", "%s", %s, "%s", "%s");' % ( p.fs_type, common.PARTITION_TYPES[p.fs_type], self._GetSlotSuffixDeviceForEntry(p), p.mount_point, mount_flags)) self.mounts.add(p.mount_point) Loading @@ -242,8 +262,9 @@ class EdifyGenerator(object): raise ValueError("Partition %s cannot be tuned\n" % (partition,)) self.script.append( 'tune2fs(' + "".join(['"%s", ' % (i,) for i in options]) + '"%s") || abort("E%d: Failed to tune partition %s");' % ( p.device, common.ErrorCode.TUNE_PARTITION_FAILURE, partition)) '%s) || abort("E%d: Failed to tune partition %s");' % ( self._GetSlotSuffixDeviceForEntry(p), common.ErrorCode.TUNE_PARTITION_FAILURE, partition)) def FormatPartition(self, partition): """Format the given partition, specified by its mount point (eg, Loading @@ -252,18 +273,19 @@ class EdifyGenerator(object): fstab = self.fstab if fstab: p = fstab[partition] self.script.append('format("%s", "%s", "%s", "%s", "%s");' % self.script.append('format("%s", "%s", %s, "%s", "%s");' % (p.fs_type, common.PARTITION_TYPES[p.fs_type], p.device, p.length, p.mount_point)) self._GetSlotSuffixDeviceForEntry(p), p.length, p.mount_point)) def WipeBlockDevice(self, partition): if partition not in ("/system", "/vendor"): raise ValueError(("WipeBlockDevice doesn't work on %s\n") % (partition,)) fstab = self.fstab size = self.info.get(partition.lstrip("/") + "_size", None) device = fstab[partition].device device = self._GetSlotSuffixDeviceForEntry(fstab[partition]) self.script.append('wipe_block_device("%s", %s);' % (device, size)) self.script.append('wipe_block_device(%s, %s);' % (device, size)) def ApplyPatch(self, srcfile, tgtfile, tgtsize, tgtsha1, *patchpairs): """Apply binary patches (in *patchpairs) to the given srcfile to Loading Loading @@ -296,14 +318,69 @@ class EdifyGenerator(object): self.PatchPartition(target, source, patch) def PatchPartition(self, target, source, patch): """Applies the patch to the source partition and writes it to target.""" """ Applies the patch to the source partition and writes it to target. Args: target: the target arg to patch_partition. Must be in the form of foo:bar:baz:quux source: the source arg to patch_partition. Must be in the form of foo:bar:baz:quux patch: the patch arg to patch_partition. Must be an unquoted string. """ self._CheckSecondTokenNotSlotSuffixed(target, "PatchPartitionExpr") self._CheckSecondTokenNotSlotSuffixed(source, "PatchPartitionExpr") self.PatchPartitionExpr('"%s"' % target, '"%s"' % source, '"%s"' % patch) def PatchPartitionExpr(self, target_expr, source_expr, patch_expr): """ Applies the patch to the source partition and writes it to target. Args: target_expr: an Edify expression that serves as the target arg to patch_partition. Must be evaluated to a string in the form of foo:bar:baz:quux source_expr: an Edify expression that serves as the source arg to patch_partition. Must be evaluated to a string in the form of foo:bar:baz:quux patch_expr: an Edify expression that serves as the patch arg to patch_partition. Must be evaluated to a string. """ self.script.append(self.WordWrap(( 'patch_partition("{target}",\0"{source}",\0' 'package_extract_file("{patch}")) ||\n' ' abort("E{code}: Failed to apply patch to {source}");').format( target=target, source=source, patch=patch, 'patch_partition({target},\0{source},\0' 'package_extract_file({patch})) ||\n' ' abort(concat(' ' "E{code}: Failed to apply patch to ",{source}));').format( target=target_expr, source=source_expr, patch=patch_expr, code=common.ErrorCode.APPLY_PATCH_FAILURE))) def _GetSlotSuffixDeviceForEntry(self, entry=None): """ Args: entry: the fstab entry of device "foo" Returns: An edify expression. Caller must not quote result. If foo is slot suffixed, it returns 'add_slot_suffix("foo")' Otherwise it returns '"foo"' (quoted) """ assert entry is not None if entry.slotselect: return 'add_slot_suffix("%s")' % entry.device return '"%s"' % entry.device def _CheckSecondTokenNotSlotSuffixed(self, s, fn): lst = s.split(':') assert(len(s) == 4), "{} does not contain 4 tokens".format(s) if self.fstab: entry = common.GetEntryForDevice(s[1]) if entry is not None: assert not entry.slotselect, \ "Use %s because %s is slot suffixed" % (fn, s[1]) def WriteRawImage(self, mount_point, fn, mapfn=None): """Write the given package file into the partition for the given mount point.""" Loading @@ -312,15 +389,16 @@ class EdifyGenerator(object): if fstab: p = fstab[mount_point] partition_type = common.PARTITION_TYPES[p.fs_type] args = {'device': p.device, 'fn': fn} device = self._GetSlotSuffixDeviceForEntry(p) args = {'device': device, 'fn': fn} if partition_type == "EMMC": if mapfn: args["map"] = mapfn self.script.append( 'package_extract_file("%(fn)s", "%(device)s", "%(map)s");' % args) 'package_extract_file("%(fn)s", %(device)s, "%(map)s");' % args) else: self.script.append( 'package_extract_file("%(fn)s", "%(device)s");' % args) 'package_extract_file("%(fn)s", %(device)s);' % args) else: raise ValueError( "don't know how to write \"%s\" partitions" % p.fs_type) Loading
tools/releasetools/ota_from_target_files.py +12 −12 Original line number Diff line number Diff line Loading @@ -1458,7 +1458,8 @@ else if get_stage("%(bcb_dev)s") != "3/3" then required_cache_sizes = [diff.required_cache for diff in block_diff_dict.values()] if updating_boot: boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info) boot_type, boot_device_expr = common.GetTypeAndDeviceExpr("/boot", source_info) d = common.Difference(target_boot, source_boot) _, _, d = d.ComputePatch() if d is None: Loading @@ -1473,11 +1474,11 @@ else if get_stage("%(bcb_dev)s") != "3/3" then common.ZipWriteStr(output_zip, "boot.img.p", d) script.PatchPartitionCheck( "{}:{}:{}:{}".format( boot_type, boot_device, target_boot.size, target_boot.sha1), "{}:{}:{}:{}".format( boot_type, boot_device, source_boot.size, source_boot.sha1)) target_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, target_boot.size, target_boot.sha1) source_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, source_boot.size, source_boot.sha1) script.PatchPartitionExprCheck(target_expr, source_expr) required_cache_sizes.append(target_boot.size) Loading Loading @@ -1545,12 +1546,11 @@ else logger.info("boot image changed; including patch.") script.Print("Patching boot image...") script.ShowProgress(0.1, 10) script.PatchPartition( '{}:{}:{}:{}'.format( boot_type, boot_device, target_boot.size, target_boot.sha1), '{}:{}:{}:{}'.format( boot_type, boot_device, source_boot.size, source_boot.sha1), 'boot.img.p') target_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, target_boot.size, target_boot.sha1) source_expr = 'concat("{}:",{},":{}:{}")'.format( boot_type, boot_device_expr, source_boot.size, source_boot.sha1) script.PatchPartitionExpr(target_expr, source_expr, '"boot.img.p"') else: logger.info("boot image unchanged; skipping.") Loading