Merge "releasetools: Clean up build_image.py." am: 7c53288810

am: 7c8c39179a

Change-Id: Ie760c6387ac9546edc8dc301ab664ecab96db58e
This commit is contained in:
Tao Bao 2018-01-04 22:30:06 +00:00 committed by android-build-merger
commit 74713e35fe
1 changed files with 83 additions and 54 deletions

View File

@ -15,27 +15,33 @@
# limitations under the License. # limitations under the License.
""" """
Build image output_image_file from input_directory, properties_file, and target_out_dir Builds output_image from the given input_directory, properties_file,
and writes the image to target_output_directory.
Usage: build_image input_directory properties_file output_image_file target_out_dir
Usage: build_image.py input_directory properties_file output_image \\
target_output_directory
""" """
from __future__ import print_function
import os import os
import os.path import os.path
import re import re
import subprocess
import sys
import common
import shlex import shlex
import shutil import shutil
import subprocess
import sys
import common
import sparse_img import sparse_img
import tempfile
OPTIONS = common.OPTIONS OPTIONS = common.OPTIONS
FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7" FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7"
BLOCK_SIZE = 4096 BLOCK_SIZE = 4096
def RunCommand(cmd, verbose=None): def RunCommand(cmd, verbose=None):
"""Echo and run the given command. """Echo and run the given command.
@ -56,6 +62,7 @@ def RunCommand(cmd, verbose=None):
print(output.rstrip()) print(output.rstrip())
return (output, p.returncode) return (output, p.returncode)
def GetVerityFECSize(partition_size): def GetVerityFECSize(partition_size):
cmd = ["fec", "-s", str(partition_size)] cmd = ["fec", "-s", str(partition_size)]
output, exit_code = RunCommand(cmd, False) output, exit_code = RunCommand(cmd, False)
@ -63,6 +70,7 @@ def GetVerityFECSize(partition_size):
return False, 0 return False, 0
return True, int(output) return True, int(output)
def GetVerityTreeSize(partition_size): def GetVerityTreeSize(partition_size):
cmd = ["build_verity_tree", "-s", str(partition_size)] cmd = ["build_verity_tree", "-s", str(partition_size)]
output, exit_code = RunCommand(cmd, False) output, exit_code = RunCommand(cmd, False)
@ -70,6 +78,7 @@ def GetVerityTreeSize(partition_size):
return False, 0 return False, 0
return True, int(output) return True, int(output)
def GetVerityMetadataSize(partition_size): def GetVerityMetadataSize(partition_size):
cmd = ["system/extras/verity/build_verity_metadata.py", "size", cmd = ["system/extras/verity/build_verity_metadata.py", "size",
str(partition_size)] str(partition_size)]
@ -78,6 +87,7 @@ def GetVerityMetadataSize(partition_size):
return False, 0 return False, 0
return True, int(output) return True, int(output)
def GetVeritySize(partition_size, fec_supported): def GetVeritySize(partition_size, fec_supported):
success, verity_tree_size = GetVerityTreeSize(partition_size) success, verity_tree_size = GetVerityTreeSize(partition_size)
if not success: if not success:
@ -93,16 +103,19 @@ def GetVeritySize(partition_size, fec_supported):
return verity_size + fec_size return verity_size + fec_size
return verity_size return verity_size
def GetSimgSize(image_file): def GetSimgSize(image_file):
simg = sparse_img.SparseImage(image_file, build_map=False) simg = sparse_img.SparseImage(image_file, build_map=False)
return simg.blocksize * simg.total_blocks return simg.blocksize * simg.total_blocks
def ZeroPadSimg(image_file, pad_size): def ZeroPadSimg(image_file, pad_size):
blocks = pad_size // BLOCK_SIZE blocks = pad_size // BLOCK_SIZE
print("Padding %d blocks (%d bytes)" % (blocks, pad_size)) print("Padding %d blocks (%d bytes)" % (blocks, pad_size))
simg = sparse_img.SparseImage(image_file, mode="r+b", build_map=False) simg = sparse_img.SparseImage(image_file, mode="r+b", build_map=False)
simg.AppendFillChunk(0, blocks) simg.AppendFillChunk(0, blocks)
def AVBCalcMaxImageSize(avbtool, footer_type, partition_size, additional_args): def AVBCalcMaxImageSize(avbtool, footer_type, partition_size, additional_args):
"""Calculates max image size for a given partition size. """Calculates max image size for a given partition size.
@ -115,8 +128,8 @@ def AVBCalcMaxImageSize(avbtool, footer_type, partition_size, additional_args):
Returns: Returns:
The maximum image size or 0 if an error occurred. The maximum image size or 0 if an error occurred.
""" """
cmd =[avbtool, "add_%s_footer" % footer_type, cmd = [avbtool, "add_%s_footer" % footer_type,
"--partition_size", partition_size, "--calc_max_image_size"] "--partition_size", partition_size, "--calc_max_image_size"]
cmd.extend(shlex.split(additional_args)) cmd.extend(shlex.split(additional_args))
(output, exit_code) = RunCommand(cmd) (output, exit_code) = RunCommand(cmd)
@ -125,6 +138,7 @@ def AVBCalcMaxImageSize(avbtool, footer_type, partition_size, additional_args):
else: else:
return int(output) return int(output)
def AVBAddFooter(image_path, avbtool, footer_type, partition_size, def AVBAddFooter(image_path, avbtool, footer_type, partition_size,
partition_name, key_path, algorithm, salt, partition_name, key_path, algorithm, salt,
additional_args): additional_args):
@ -140,14 +154,15 @@ def AVBAddFooter(image_path, avbtool, footer_type, partition_size,
algorithm: Name of algorithm to use or None. algorithm: Name of algorithm to use or None.
salt: The salt to use (a hexadecimal string) or None. salt: The salt to use (a hexadecimal string) or None.
additional_args: Additional arguments to pass to 'avbtool additional_args: Additional arguments to pass to 'avbtool
add_hashtree_image'. add_hashtree_image'.
Returns: Returns:
True if the operation succeeded. True if the operation succeeded.
""" """
cmd =[avbtool, "add_%s_footer" % footer_type, cmd = [avbtool, "add_%s_footer" % footer_type,
"--partition_size", partition_size, "--partition_size", partition_size,
"--partition_name", partition_name, "--partition_name", partition_name,
"--image", image_path] "--image", image_path]
if key_path and algorithm: if key_path and algorithm:
cmd.extend(["--key", key_path, "--algorithm", algorithm]) cmd.extend(["--key", key_path, "--algorithm", algorithm])
@ -159,12 +174,15 @@ def AVBAddFooter(image_path, avbtool, footer_type, partition_size,
(_, exit_code) = RunCommand(cmd) (_, exit_code) = RunCommand(cmd)
return exit_code == 0 return exit_code == 0
def AdjustPartitionSizeForVerity(partition_size, fec_supported): def AdjustPartitionSizeForVerity(partition_size, fec_supported):
"""Modifies the provided partition size to account for the verity metadata. """Modifies the provided partition size to account for the verity metadata.
This information is used to size the created image appropriately. This information is used to size the created image appropriately.
Args: Args:
partition_size: the size of the partition to be verified. partition_size: the size of the partition to be verified.
Returns: Returns:
A tuple of the size of the partition adjusted for verity metadata, and A tuple of the size of the partition adjusted for verity metadata, and
the size of verity metadata. the size of verity metadata.
@ -201,30 +219,34 @@ def AdjustPartitionSizeForVerity(partition_size, fec_supported):
AdjustPartitionSizeForVerity.results[key] = (result, verity_size) AdjustPartitionSizeForVerity.results[key] = (result, verity_size)
return (result, verity_size) return (result, verity_size)
AdjustPartitionSizeForVerity.results = {} AdjustPartitionSizeForVerity.results = {}
def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path, def BuildVerityFEC(sparse_image_path, verity_path, verity_fec_path,
padding_size): padding_size):
cmd = ["fec", "-e", "-p", str(padding_size), sparse_image_path, cmd = ["fec", "-e", "-p", str(padding_size), sparse_image_path,
verity_path, verity_fec_path] verity_path, verity_fec_path]
output, exit_code = RunCommand(cmd) output, exit_code = RunCommand(cmd)
if exit_code != 0: if exit_code != 0:
print "Could not build FEC data! Error: %s" % output print("Could not build FEC data! Error: %s" % output)
return False return False
return True return True
def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict): def BuildVerityTree(sparse_image_path, verity_image_path, prop_dict):
cmd = ["build_verity_tree", "-A", FIXED_SALT, sparse_image_path, cmd = ["build_verity_tree", "-A", FIXED_SALT, sparse_image_path,
verity_image_path] verity_image_path]
output, exit_code = RunCommand(cmd) output, exit_code = RunCommand(cmd)
if exit_code != 0: if exit_code != 0:
print "Could not build verity tree! Error: %s" % output print("Could not build verity tree! Error: %s" % output)
return False return False
root, salt = output.split() root, salt = output.split()
prop_dict["verity_root_hash"] = root prop_dict["verity_root_hash"] = root
prop_dict["verity_salt"] = salt prop_dict["verity_salt"] = salt
return True return True
def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt, def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
block_device, signer_path, key, signer_args, block_device, signer_path, key, signer_args,
verity_disable): verity_disable):
@ -237,10 +259,11 @@ def BuildVerityMetadata(image_size, verity_metadata_path, root_hash, salt,
cmd.append("--verity_disable") cmd.append("--verity_disable")
output, exit_code = RunCommand(cmd) output, exit_code = RunCommand(cmd)
if exit_code != 0: if exit_code != 0:
print "Could not build verity metadata! Error: %s" % output print("Could not build verity metadata! Error: %s" % output)
return False return False
return True return True
def Append2Simg(sparse_image_path, unsparse_image_path, error_message): def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
"""Appends the unsparse image to the given sparse image. """Appends the unsparse image to the given sparse image.
@ -253,18 +276,23 @@ def Append2Simg(sparse_image_path, unsparse_image_path, error_message):
cmd = ["append2simg", sparse_image_path, unsparse_image_path] cmd = ["append2simg", sparse_image_path, unsparse_image_path]
output, exit_code = RunCommand(cmd) output, exit_code = RunCommand(cmd)
if exit_code != 0: if exit_code != 0:
print "%s: %s" % (error_message, output) print("%s: %s" % (error_message, output))
return False return False
return True return True
def Append(target, file_to_append, error_message): def Append(target, file_to_append, error_message):
# appending file_to_append to target """Appends file_to_append to target."""
with open(target, "a") as out_file: try:
with open(file_to_append, "r") as input_file: with open(target, "a") as out_file, open(file_to_append, "r") as input_file:
for line in input_file: for line in input_file:
out_file.write(line) out_file.write(line)
except IOError:
print(error_message)
return False
return True return True
def BuildVerifiedImage(data_image_path, verity_image_path, def BuildVerifiedImage(data_image_path, verity_image_path,
verity_metadata_path, verity_fec_path, verity_metadata_path, verity_fec_path,
padding_size, fec_supported): padding_size, fec_supported):
@ -286,6 +314,7 @@ def BuildVerifiedImage(data_image_path, verity_image_path,
return False return False
return True return True
def UnsparseImage(sparse_image_path, replace=True): def UnsparseImage(sparse_image_path, replace=True):
img_dir = os.path.dirname(sparse_image_path) img_dir = os.path.dirname(sparse_image_path)
unsparse_image_path = "unsparse_" + os.path.basename(sparse_image_path) unsparse_image_path = "unsparse_" + os.path.basename(sparse_image_path)
@ -302,6 +331,7 @@ def UnsparseImage(sparse_image_path, replace=True):
return False, None return False, None
return True, unsparse_image_path return True, unsparse_image_path
def MakeVerityEnabledImage(out_file, fec_supported, prop_dict): def MakeVerityEnabledImage(out_file, fec_supported, prop_dict):
"""Creates an image that is verifiable using dm-verity. """Creates an image that is verifiable using dm-verity.
@ -360,14 +390,12 @@ def MakeVerityEnabledImage(out_file, fec_supported, prop_dict):
return True return True
def ConvertBlockMapToBaseFs(block_map_file): def ConvertBlockMapToBaseFs(block_map_file):
base_fs_file = common.MakeTempFile(prefix="script_gen_", suffix=".base_fs") base_fs_file = common.MakeTempFile(prefix="script_gen_", suffix=".base_fs")
convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file] convert_command = ["blk_alloc_to_base_fs", block_map_file, base_fs_file]
(_, exit_code) = RunCommand(convert_command) (_, exit_code) = RunCommand(convert_command)
if exit_code != 0: return base_fs_file if exit_code == 0 else None
return None
return base_fs_file
def CheckHeadroom(ext4fs_output, prop_dict): def CheckHeadroom(ext4fs_output, prop_dict):
@ -396,7 +424,8 @@ def CheckHeadroom(ext4fs_output, prop_dict):
ext4fs_stats = re.compile( ext4fs_stats = re.compile(
r'Created filesystem with .* (?P<used_blocks>[0-9]+)/' r'Created filesystem with .* (?P<used_blocks>[0-9]+)/'
r'(?P<total_blocks>[0-9]+) blocks') r'(?P<total_blocks>[0-9]+) blocks')
m = ext4fs_stats.match(ext4fs_output.strip().split('\n')[-1]) last_line = ext4fs_output.strip().split('\n')[-1]
m = ext4fs_stats.match(last_line)
used_blocks = int(m.groupdict().get('used_blocks')) used_blocks = int(m.groupdict().get('used_blocks'))
total_blocks = int(m.groupdict().get('total_blocks')) total_blocks = int(m.groupdict().get('total_blocks'))
headroom_blocks = int(prop_dict['partition_headroom']) / BLOCK_SIZE headroom_blocks = int(prop_dict['partition_headroom']) / BLOCK_SIZE
@ -418,7 +447,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
in_dir: path of input directory. in_dir: path of input directory.
prop_dict: property dictionary. prop_dict: property dictionary.
out_file: path of the output image file. out_file: path of the output image file.
target_out: path of the product out directory to read device specific FS config files. target_out: path of the product out directory to read device specific FS
config files.
Returns: Returns:
True iff the image is built successfully. True iff the image is built successfully.
@ -427,10 +457,10 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
# /system and the ramdisk, and can be mounted at the root of the file system. # /system and the ramdisk, and can be mounted at the root of the file system.
origin_in = in_dir origin_in = in_dir
fs_config = prop_dict.get("fs_config") fs_config = prop_dict.get("fs_config")
if (prop_dict.get("system_root_image") == "true" if (prop_dict.get("system_root_image") == "true" and
and prop_dict["mount_point"] == "system"): prop_dict["mount_point"] == "system"):
in_dir = common.MakeTempDir() in_dir = common.MakeTempDir()
# Change the mount point to "/" # Change the mount point to "/".
prop_dict["mount_point"] = "/" prop_dict["mount_point"] = "/"
if fs_config: if fs_config:
# We need to merge the fs_config files of system and ramdisk. # We need to merge the fs_config files of system and ramdisk.
@ -446,7 +476,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
build_command = [] build_command = []
fs_type = prop_dict.get("fs_type", "") fs_type = prop_dict.get("fs_type", "")
run_fsck = False run_e2fsck = False
fs_spans_partition = True fs_spans_partition = True
if fs_type.startswith("squash"): if fs_type.startswith("squash"):
@ -460,8 +490,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
# verified. # verified.
if verity_supported and is_verity_partition: if verity_supported and is_verity_partition:
partition_size = int(prop_dict.get("partition_size")) partition_size = int(prop_dict.get("partition_size"))
(adjusted_size, verity_size) = AdjustPartitionSizeForVerity(partition_size, (adjusted_size, verity_size) = AdjustPartitionSizeForVerity(
verity_fec_supported) partition_size, verity_fec_supported)
if not adjusted_size: if not adjusted_size:
return False return False
prop_dict["partition_size"] = str(adjusted_size) prop_dict["partition_size"] = str(adjusted_size)
@ -480,8 +510,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
partition_size = prop_dict["partition_size"] partition_size = prop_dict["partition_size"]
# avb_add_hash_footer_args or avb_add_hashtree_footer_args. # avb_add_hash_footer_args or avb_add_hashtree_footer_args.
additional_args = prop_dict["avb_add_" + avb_footer_type + "_footer_args"] additional_args = prop_dict["avb_add_" + avb_footer_type + "_footer_args"]
max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type, partition_size, max_image_size = AVBCalcMaxImageSize(avbtool, avb_footer_type,
additional_args) partition_size, additional_args)
if max_image_size == 0: if max_image_size == 0:
return False return False
prop_dict["partition_size"] = str(max_image_size) prop_dict["partition_size"] = str(max_image_size)
@ -491,7 +521,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
build_command = [prop_dict["ext_mkuserimg"]] build_command = [prop_dict["ext_mkuserimg"]]
if "extfs_sparse_flag" in prop_dict: if "extfs_sparse_flag" in prop_dict:
build_command.append(prop_dict["extfs_sparse_flag"]) build_command.append(prop_dict["extfs_sparse_flag"])
run_fsck = True run_e2fsck = True
build_command.extend([in_dir, out_file, fs_type, build_command.extend([in_dir, out_file, fs_type,
prop_dict["mount_point"]]) prop_dict["mount_point"]])
build_command.append(prop_dict["partition_size"]) build_command.append(prop_dict["partition_size"])
@ -545,7 +575,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]]) build_command.extend(["-zo", prop_dict["squashfs_compressor_opt"]])
if "squashfs_block_size" in prop_dict: if "squashfs_block_size" in prop_dict:
build_command.extend(["-b", prop_dict["squashfs_block_size"]]) build_command.extend(["-b", prop_dict["squashfs_block_size"]])
if "squashfs_disable_4k_align" in prop_dict and prop_dict.get("squashfs_disable_4k_align") == "true": if prop_dict.get("squashfs_disable_4k_align") == "true":
build_command.extend(["-a"]) build_command.extend(["-a"])
elif fs_type.startswith("f2fs"): elif fs_type.startswith("f2fs"):
build_command = ["mkf2fsuserimg.sh"] build_command = ["mkf2fsuserimg.sh"]
@ -575,18 +605,14 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
shutil.rmtree(staging_system, ignore_errors=True) shutil.rmtree(staging_system, ignore_errors=True)
shutil.copytree(origin_in, staging_system, symlinks=True) shutil.copytree(origin_in, staging_system, symlinks=True)
ext4fs_output = None (mkfs_output, exit_code) = RunCommand(build_command)
if fs_type.startswith("ext4"):
(ext4fs_output, exit_code) = RunCommand(build_command)
else:
(_, exit_code) = RunCommand(build_command)
if exit_code != 0: if exit_code != 0:
print("Error: '%s' failed with exit code %d" % (build_command, exit_code)) print("Error: '%s' failed with exit code %d" % (build_command, exit_code))
return False return False
# Check if there's enough headroom space available for ext4 image. # Check if there's enough headroom space available for ext4 image.
if "partition_headroom" in prop_dict and fs_type.startswith("ext4"): if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):
if not CheckHeadroom(ext4fs_output, prop_dict): if not CheckHeadroom(mkfs_output, prop_dict):
return False return False
if not fs_spans_partition: if not fs_spans_partition:
@ -600,7 +626,7 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
if verity_supported and is_verity_partition: if verity_supported and is_verity_partition:
ZeroPadSimg(out_file, partition_size - image_size) ZeroPadSimg(out_file, partition_size - image_size)
# create the verified image if this is to be verified # Create the verified image if this is to be verified.
if verity_supported and is_verity_partition: if verity_supported and is_verity_partition:
if not MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict): if not MakeVerityEnabledImage(out_file, verity_fec_supported, prop_dict):
return False return False
@ -616,11 +642,12 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
salt = prop_dict.get("avb_salt") salt = prop_dict.get("avb_salt")
# avb_add_hash_footer_args or avb_add_hashtree_footer_args # avb_add_hash_footer_args or avb_add_hashtree_footer_args
additional_args = prop_dict["avb_add_" + avb_footer_type + "_footer_args"] additional_args = prop_dict["avb_add_" + avb_footer_type + "_footer_args"]
if not AVBAddFooter(out_file, avbtool, avb_footer_type, original_partition_size, if not AVBAddFooter(out_file, avbtool, avb_footer_type,
partition_name, key_path, algorithm, salt, additional_args): original_partition_size, partition_name, key_path,
algorithm, salt, additional_args):
return False return False
if run_fsck and prop_dict.get("skip_fsck") != "true": if run_e2fsck and prop_dict.get("skip_fsck") != "true":
success, unsparse_image = UnsparseImage(out_file, replace=False) success, unsparse_image = UnsparseImage(out_file, replace=False)
if not success: if not success:
return False return False
@ -632,7 +659,8 @@ def BuildImage(in_dir, prop_dict, out_file, target_out=None):
os.remove(unsparse_image) os.remove(unsparse_image)
if exit_code != 0: if exit_code != 0:
print("Error: '%s' failed with exit code %d" % (e2fsck_command, exit_code)) print("Error: '%s' failed with exit code %d" % (e2fsck_command,
exit_code))
return False return False
return True return True
@ -699,7 +727,8 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
copy_prop("system_base_fs_file", "base_fs_file") copy_prop("system_base_fs_file", "base_fs_file")
copy_prop("system_extfs_inode_count", "extfs_inode_count") copy_prop("system_extfs_inode_count", "extfs_inode_count")
elif mount_point == "system_other": elif mount_point == "system_other":
# We inherit the selinux policies of /system since we contain some of its files. # We inherit the selinux policies of /system since we contain some of its
# files.
d["mount_point"] = "system" d["mount_point"] = "system"
copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable") copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")
copy_prop("avb_system_add_hashtree_footer_args", copy_prop("avb_system_add_hashtree_footer_args",
@ -721,7 +750,7 @@ def ImagePropFromGlobalDict(glob_dict, mount_point):
copy_prop("fs_type", "fs_type") copy_prop("fs_type", "fs_type")
copy_prop("userdata_fs_type", "fs_type") copy_prop("userdata_fs_type", "fs_type")
copy_prop("userdata_size", "partition_size") copy_prop("userdata_size", "partition_size")
copy_prop("flash_logical_block_size","flash_logical_block_size") copy_prop("flash_logical_block_size", "flash_logical_block_size")
copy_prop("flash_erase_block_size", "flash_erase_block_size") copy_prop("flash_erase_block_size", "flash_erase_block_size")
elif mount_point == "cache": elif mount_point == "cache":
copy_prop("cache_fs_type", "fs_type") copy_prop("cache_fs_type", "fs_type")
@ -767,7 +796,7 @@ def LoadGlobalDict(filename):
def main(argv): def main(argv):
if len(argv) != 4: if len(argv) != 4:
print __doc__ print(__doc__)
sys.exit(1) sys.exit(1)
in_dir = argv[0] in_dir = argv[0]
@ -796,14 +825,14 @@ def main(argv):
elif image_filename == "oem.img": elif image_filename == "oem.img":
mount_point = "oem" mount_point = "oem"
else: else:
print >> sys.stderr, "error: unknown image file name ", image_filename print("error: unknown image file name ", image_filename, file=sys.stderr)
sys.exit(1) sys.exit(1)
image_properties = ImagePropFromGlobalDict(glob_dict, mount_point) image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
if not BuildImage(in_dir, image_properties, out_file, target_out): if not BuildImage(in_dir, image_properties, out_file, target_out):
print >> sys.stderr, "error: failed to build %s from %s" % (out_file, print("error: failed to build %s from %s" % (out_file, in_dir),
in_dir) file=sys.stderr)
sys.exit(1) sys.exit(1)