1bd93d425aedef907d8fef643b748897e9c7dceebYing Wang#!/usr/bin/env python
2bd93d425aedef907d8fef643b748897e9c7dceebYing Wang#
3bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# Copyright (C) 2011 The Android Open Source Project
4bd93d425aedef907d8fef643b748897e9c7dceebYing Wang#
5bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# Licensed under the Apache License, Version 2.0 (the "License");
6bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# you may not use this file except in compliance with the License.
7bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# You may obtain a copy of the License at
8bd93d425aedef907d8fef643b748897e9c7dceebYing Wang#
9bd93d425aedef907d8fef643b748897e9c7dceebYing Wang#      http://www.apache.org/licenses/LICENSE-2.0
10bd93d425aedef907d8fef643b748897e9c7dceebYing Wang#
11bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# Unless required by applicable law or agreed to in writing, software
12bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# distributed under the License is distributed on an "AS IS" BASIS,
13bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# See the License for the specific language governing permissions and
15bd93d425aedef907d8fef643b748897e9c7dceebYing Wang# limitations under the License.
16bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
17bd93d425aedef907d8fef643b748897e9c7dceebYing Wang"""
18bd93d425aedef907d8fef643b748897e9c7dceebYing WangBuild image output_image_file from input_directory and properties_file.
19bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
20bd93d425aedef907d8fef643b748897e9c7dceebYing WangUsage:  build_image input_directory properties_file output_image_file
21bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
22bd93d425aedef907d8fef643b748897e9c7dceebYing Wang"""
23bd93d425aedef907d8fef643b748897e9c7dceebYing Wangimport os
2469e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wangimport os.path
25bd93d425aedef907d8fef643b748897e9c7dceebYing Wangimport subprocess
26bd93d425aedef907d8fef643b748897e9c7dceebYing Wangimport sys
27bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
2869e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wangdef RunCommand(cmd):
2969e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  """ Echo and run the given command
3069e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang
3169e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  Args:
3269e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    cmd: the command represented as a list of strings.
3369e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  Returns:
3469e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    The exit code.
3569e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  """
3669e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  print "Running: ", " ".join(cmd)
3769e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  p = subprocess.Popen(cmd)
3869e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  p.communicate()
3969e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  return p.returncode
40bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
41bd93d425aedef907d8fef643b748897e9c7dceebYing Wangdef BuildImage(in_dir, prop_dict, out_file):
42bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  """Build an image to out_file from in_dir with property prop_dict.
43bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
44bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  Args:
45bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    in_dir: path of input directory.
46bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    prop_dict: property dictionary.
47bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    out_file: path of the output image file.
48bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
49bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  Returns:
50bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    True iff the image is built successfully.
51bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  """
52bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  build_command = []
53bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  fs_type = prop_dict.get("fs_type", "")
5469e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  run_fsck = False
55bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  if fs_type.startswith("ext"):
56bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    build_command = ["mkuserimg.sh"]
57bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    if "extfs_sparse_flag" in prop_dict:
58bd93d425aedef907d8fef643b748897e9c7dceebYing Wang      build_command.append(prop_dict["extfs_sparse_flag"])
5969e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang      run_fsck = True
60bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    build_command.extend([in_dir, out_file, fs_type,
61bd93d425aedef907d8fef643b748897e9c7dceebYing Wang                          prop_dict["mount_point"]])
62bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    if "partition_size" in prop_dict:
63bd93d425aedef907d8fef643b748897e9c7dceebYing Wang      build_command.append(prop_dict["partition_size"])
64f32dc71e49656e16fc8e178f7436706a47e8af52Kenny Root    if "selinux_fc" in prop_dict:
65f32dc71e49656e16fc8e178f7436706a47e8af52Kenny Root      build_command.append(prop_dict["selinux_fc"])
66bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  else:
67bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    build_command = ["mkyaffs2image", "-f"]
68bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    if prop_dict.get("mkyaffs2_extra_flags", None):
69bd93d425aedef907d8fef643b748897e9c7dceebYing Wang      build_command.extend(prop_dict["mkyaffs2_extra_flags"].split())
70bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    build_command.append(in_dir)
71bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    build_command.append(out_file)
72f32dc71e49656e16fc8e178f7436706a47e8af52Kenny Root    if "selinux_fc" in prop_dict:
73f32dc71e49656e16fc8e178f7436706a47e8af52Kenny Root      build_command.append(prop_dict["selinux_fc"])
74f32dc71e49656e16fc8e178f7436706a47e8af52Kenny Root      build_command.append(prop_dict["mount_point"])
75bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
7669e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  exit_code = RunCommand(build_command)
7769e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  if exit_code != 0:
7869e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    return False
7969e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang
806a42a25429a3313f87c8ca5154c99c7acd0eccdfYing Wang  if run_fsck and prop_dict.get("skip_fsck") != "true":
8169e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    # Inflate the sparse image
8269e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    unsparse_image = os.path.join(
8369e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang        os.path.dirname(out_file), "unsparse_" + os.path.basename(out_file))
8469e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    inflate_command = ["simg2img", out_file, unsparse_image]
8569e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    exit_code = RunCommand(inflate_command)
8669e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    if exit_code != 0:
8769e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang      os.remove(unsparse_image)
8869e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang      return False
8969e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang
9069e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    # Run e2fsck on the inflated image file
9169e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]
9269e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    exit_code = RunCommand(e2fsck_command)
9369e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang
9469e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang    os.remove(unsparse_image)
9569e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang
9669e9b4d6d1e9647c3f9c011bad0f22138911a413Ying Wang  return exit_code == 0
97bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
98bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
99bd93d425aedef907d8fef643b748897e9c7dceebYing Wangdef ImagePropFromGlobalDict(glob_dict, mount_point):
100bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  """Build an image property dictionary from the global dictionary.
101bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
102bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  Args:
103bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    glob_dict: the global dictionary from the build system.
104bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    mount_point: such as "system", "data" etc.
105bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  """
106bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  d = {}
1079f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang
1089f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang  def copy_prop(src_p, dest_p):
1099f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    if src_p in glob_dict:
1109f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang      d[dest_p] = str(glob_dict[src_p])
1119f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang
112bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  common_props = (
113bd93d425aedef907d8fef643b748897e9c7dceebYing Wang      "extfs_sparse_flag",
114bd93d425aedef907d8fef643b748897e9c7dceebYing Wang      "mkyaffs2_extra_flags",
115f32dc71e49656e16fc8e178f7436706a47e8af52Kenny Root      "selinux_fc",
1166a42a25429a3313f87c8ca5154c99c7acd0eccdfYing Wang      "skip_fsck",
117bd93d425aedef907d8fef643b748897e9c7dceebYing Wang      )
118bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  for p in common_props:
1199f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    copy_prop(p, p)
120bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
121bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  d["mount_point"] = mount_point
122bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  if mount_point == "system":
1239f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    copy_prop("fs_type", "fs_type")
1249f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    copy_prop("system_size", "partition_size")
125bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  elif mount_point == "data":
1269f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    copy_prop("fs_type", "fs_type")
1279f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    copy_prop("userdata_size", "partition_size")
1289f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang  elif mount_point == "cache":
1299f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    copy_prop("cache_fs_type", "fs_type")
1309f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    copy_prop("cache_size", "partition_size")
131a0febe5e6de24ffee5cd32944d5a1814cf312efbYing Wang  elif mount_point == "vendor":
132a0febe5e6de24ffee5cd32944d5a1814cf312efbYing Wang    copy_prop("vendor_fs_type", "fs_type")
133a0febe5e6de24ffee5cd32944d5a1814cf312efbYing Wang    copy_prop("vendor_size", "partition_size")
134bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
135bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  return d
136bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
137bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
138bd93d425aedef907d8fef643b748897e9c7dceebYing Wangdef LoadGlobalDict(filename):
139bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  """Load "name=value" pairs from filename"""
140bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  d = {}
141bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  f = open(filename)
142bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  for line in f:
143bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    line = line.strip()
144bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    if not line or line.startswith("#"):
145bd93d425aedef907d8fef643b748897e9c7dceebYing Wang      continue
146bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    k, v = line.split("=", 1)
147bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    d[k] = v
148bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  f.close()
149bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  return d
150bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
151bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
152bd93d425aedef907d8fef643b748897e9c7dceebYing Wangdef main(argv):
153bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  if len(argv) != 3:
154bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    print __doc__
155bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    sys.exit(1)
156bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
157bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  in_dir = argv[0]
158bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  glob_dict_file = argv[1]
159bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  out_file = argv[2]
160bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
161bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  glob_dict = LoadGlobalDict(glob_dict_file)
162bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  image_filename = os.path.basename(out_file)
163bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  mount_point = ""
164bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  if image_filename == "system.img":
165bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    mount_point = "system"
166bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  elif image_filename == "userdata.img":
167bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    mount_point = "data"
1689f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang  elif image_filename == "cache.img":
1699f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    mount_point = "cache"
170a0febe5e6de24ffee5cd32944d5a1814cf312efbYing Wang  elif image_filename == "vendor.img":
171a0febe5e6de24ffee5cd32944d5a1814cf312efbYing Wang    mount_point = "vendor"
1729f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang  else:
1739f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    print >> sys.stderr, "error: unknown image file name ", image_filename
1749f8e8db188371cb3787a91a03d193f87ad244ea3Ying Wang    exit(1)
175bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
176bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)
177bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  if not BuildImage(in_dir, image_properties, out_file):
178bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    print >> sys.stderr, "error: failed to build %s from %s" % (out_file, in_dir)
179bd93d425aedef907d8fef643b748897e9c7dceebYing Wang    exit(1)
180bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
181bd93d425aedef907d8fef643b748897e9c7dceebYing Wang
182bd93d425aedef907d8fef643b748897e9c7dceebYing Wangif __name__ == '__main__':
183bd93d425aedef907d8fef643b748897e9c7dceebYing Wang  main(sys.argv[1:])
184