• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2#
3# Copyright (C) 2023 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
17import argparse
18import glob
19import os
20import subprocess
21import tempfile
22
23from build_chd_debug_ramdisk import build_chd_debug_ramdisk, ImageOptions
24from build_chd_utils import copy_files, merge_chd_sepolicy, unzip_otatools
25
26"""Test command:
27
28WORKSPACE=out/dist && \
29python3 tools/treble/cuttlefish/build_cf_hybrid_device.py \
30    --build_id 123456 \
31    --otatools_zip $WORKSPACE/otatools.zip \
32    --target chd-target \
33    --output_dir $WORKSPACE \
34    --framework_target_files_zip $WORKSPACE/device-target_files-*.zip \
35    --vendor_target_files_zip $WORKSPACE/cf_arm64_only_phone-target_files-*.zip
36"""
37
38
39def _parse_args() -> argparse.Namespace:
40  """Parse the arguments for building cuttlefish hybrid devices.
41
42  Returns:
43    An object of the parsed arguments.
44  """
45  parser = argparse.ArgumentParser()
46
47  parser.add_argument('--build_id', default='',
48                      help='Build id.')
49  parser.add_argument('--target', required=True,
50                      help='Target name of the cuttlefish hybrid build.')
51  parser.add_argument('--otatools_zip', required=True,
52                      help='Path to the otatools.zip.')
53  parser.add_argument('--output_dir', required=True,
54                      help='Path to the output directory of the hybrid build.')
55  parser.add_argument('--framework_target_files_zip', required=True,
56                      help='glob pattern of framework target_files zip.')
57  parser.add_argument('--vendor_target_files_zip', required=True,
58                      help='glob pattern of vendor target_files zip.')
59  parser.add_argument('--copy_file', action='append', default=[],
60                      help='The file to be copied to output directory. '
61                           'The format is <src glob pattern>:<dst path>.')
62  return parser.parse_args()
63
64
65def run(temp_dir: str) -> None:
66  args = _parse_args()
67
68  # unzip otatools
69  otatools = os.path.join(temp_dir, 'otatools')
70  unzip_otatools(args.otatools_zip, otatools)
71
72  # get framework and vendor target files
73  matched_framework_target_files = glob.glob(args.framework_target_files_zip)
74  if not matched_framework_target_files:
75    raise ValueError('framework target files zip '
76                     f'{args.framework_target_files_zip} not found.')
77  matched_vendor_target_files = glob.glob(args.vendor_target_files_zip)
78  if not matched_vendor_target_files:
79    raise ValueError('vendor target files zip '
80                     f'{args.vendor_target_files_zip} not found.')
81
82  # merge target files
83  framework_target_files = matched_framework_target_files[0]
84  vendor_target_files = matched_vendor_target_files[0]
85  build_id_str = f'-{args.build_id}' if args.build_id else ''
86  merged_target_files = os.path.join(
87      args.output_dir,
88      f'{args.target}-target_files{build_id_str}.zip')
89  command = [
90      os.path.join(otatools, 'bin', 'merge_target_files'),
91      '--path', otatools,
92      '--framework-target-files', framework_target_files,
93      '--vendor-target-files', vendor_target_files,
94      '--output-target-files', merged_target_files,
95      '--avb-resolve-rollback-index-location-conflict'
96  ]
97  subprocess.run(command, check=True)
98
99  # create images from the merged target files
100  img_zip_path = os.path.join(args.output_dir,
101                              f'{args.target}-img{build_id_str}.zip')
102  command = [
103      os.path.join(otatools, 'bin', 'img_from_target_files'),
104      merged_target_files,
105      img_zip_path]
106  subprocess.run(command, check=True)
107
108  # merge CHD debug sepolicy
109  # TODO (b/315474132): remove this when the CHD sepolicy issue is resolved.
110  chd_sepolicy = None
111  try:
112    chd_sepolicy = merge_chd_sepolicy(
113        framework_target_files, vendor_target_files, otatools, args.output_dir)
114  except Exception as error:
115    print(f'Warning - cannot generate chd_merged_sepolicy: {error}')
116
117  # copy files
118  copy_files(args.copy_file, args.output_dir)
119
120  # build the CHD vendor boot debug image by adding chd_sepolicy and
121  # chd_debug_prop (if present) into the Cuttlefish's vendor_boot-debug.img.
122  files_to_add = []
123  if chd_sepolicy and os.path.exists(chd_sepolicy):
124    files_to_add.append(f'{chd_sepolicy}:precompiled_sepolicy')
125  chd_debug_prop = os.path.join(args.output_dir, 'chd_debug.prop')
126  if os.path.exists(chd_debug_prop):
127    # rename the debug prop file as `adb_debug.prop` because this is the
128    # file name that property init expects.
129    files_to_add.append(f'{chd_debug_prop}:adb_debug.prop')
130
131  cf_debug_img = os.path.join(args.output_dir, 'vendor_boot-debug.img')
132  chd_debug_image_userdebug = 'vendor_boot-chd_debug.img'
133  chd_debug_image_user = 'vendor_boot-chd_debug_user.img'
134  if os.path.exists(cf_debug_img):
135    for image_name in [chd_debug_image_userdebug, chd_debug_image_user]:
136      image_path = os.path.join(args.output_dir, image_name)
137      image_dir = os.path.join(temp_dir, image_name)
138      os.mkdir(image_dir)
139      image_option = ImageOptions(
140          input_image=cf_debug_img,
141          output_image=image_path,
142          otatools_dir=otatools,
143          temp_dir=image_dir,
144          files_to_add=files_to_add)
145
146      # Remove userdebug_plat_sepolicy.cil from CHD's debug ramdisk to build a
147      # debug ramdisk for user builds.
148      if image_name == chd_debug_image_user:
149        image_option.files_to_remove = ['userdebug_plat_sepolicy.cil']
150
151      try:
152        build_chd_debug_ramdisk(image_option)
153      except Exception as error:
154        print(f'Warning - cannot build {image_name}: {error}')
155
156
157if __name__ == '__main__':
158  with tempfile.TemporaryDirectory() as temp_dir:
159    run(temp_dir)
160