xref: /aosp_15_r20/tools/treble/cuttlefish/build_cf_hybrid_device.py (revision 105f628577ac4ba0e277a494fbb614ed8c12a994)
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', required=True,
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  merged_target_files = os.path.join(
86      args.output_dir,
87      f'{args.target}-target_files-{args.build_id}.zip')
88  command = [
89      os.path.join(otatools, 'bin', 'merge_target_files'),
90      '--path', otatools,
91      '--framework-target-files', framework_target_files,
92      '--vendor-target-files', vendor_target_files,
93      '--output-target-files', merged_target_files,
94      '--avb-resolve-rollback-index-location-conflict'
95  ]
96  subprocess.run(command, check=True)
97
98  # create images from the merged target files
99  img_zip_path = os.path.join(args.output_dir,
100                              f'{args.target}-img-{args.build_id}.zip')
101  command = [
102      os.path.join(otatools, 'bin', 'img_from_target_files'),
103      merged_target_files,
104      img_zip_path]
105  subprocess.run(command, check=True)
106
107  # merge CHD debug sepolicy
108  # TODO (b/315474132): remove this when the CHD sepolicy issue is resolved.
109  chd_sepolicy = None
110  try:
111    chd_sepolicy = merge_chd_sepolicy(
112        framework_target_files, vendor_target_files, otatools, args.output_dir)
113  except Exception as error:
114    print(f'Warning - cannot generate chd_merged_sepolicy: {error}')
115
116  # copy files
117  copy_files(args.copy_file, args.output_dir)
118
119  # build the CHD vendor boot debug image by adding chd_sepolicy and
120  # chd_debug_prop (if present) into the Cuttlefish's vendor_boot-debug.img.
121  files_to_add = []
122  if chd_sepolicy and os.path.exists(chd_sepolicy):
123    files_to_add.append(f'{chd_sepolicy}:precompiled_sepolicy')
124  chd_debug_prop = os.path.join(args.output_dir, 'chd_debug.prop')
125  if os.path.exists(chd_debug_prop):
126    # rename the debug prop file as `adb_debug.prop` because this is the
127    # file name that property init expects.
128    files_to_add.append(f'{chd_debug_prop}:adb_debug.prop')
129
130  cf_debug_img = os.path.join(args.output_dir, 'vendor_boot-debug.img')
131  chd_debug_image_userdebug = 'vendor_boot-chd_debug.img'
132  chd_debug_image_user = 'vendor_boot-chd_debug_user.img'
133  if os.path.exists(cf_debug_img):
134    for image_name in [chd_debug_image_userdebug, chd_debug_image_user]:
135      image_path = os.path.join(args.output_dir, image_name)
136      image_dir = os.path.join(temp_dir, image_name)
137      os.mkdir(image_dir)
138      image_option = ImageOptions(
139          input_image=cf_debug_img,
140          output_image=image_path,
141          otatools_dir=otatools,
142          temp_dir=image_dir,
143          files_to_add=files_to_add)
144
145      # Remove userdebug_plat_sepolicy.cil from CHD's debug ramdisk to build a
146      # debug ramdisk for user builds.
147      if image_name == chd_debug_image_user:
148        image_option.files_to_remove = ['userdebug_plat_sepolicy.cil']
149
150      try:
151        build_chd_debug_ramdisk(image_option)
152      except Exception as error:
153        print(f'Warning - cannot build {image_name}: {error}')
154
155
156if __name__ == '__main__':
157  with tempfile.TemporaryDirectory() as temp_dir:
158    run(temp_dir)
159