xref: /aosp_15_r20/tools/treble/cuttlefish/build_chd_debug_ramdisk.py (revision 105f628577ac4ba0e277a494fbb614ed8c12a994)
1*105f6285SAndroid Build Coastguard Worker#!/usr/bin/python3
2*105f6285SAndroid Build Coastguard Worker#
3*105f6285SAndroid Build Coastguard Worker# Copyright (C) 2024 The Android Open Source Project
4*105f6285SAndroid Build Coastguard Worker#
5*105f6285SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6*105f6285SAndroid Build Coastguard Worker# use this file except in compliance with the License. You may obtain a copy of
7*105f6285SAndroid Build Coastguard Worker# the License at
8*105f6285SAndroid Build Coastguard Worker#
9*105f6285SAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
10*105f6285SAndroid Build Coastguard Worker#
11*105f6285SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
12*105f6285SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13*105f6285SAndroid Build Coastguard Worker# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14*105f6285SAndroid Build Coastguard Worker# License for the specific language governing permissions and limitations under
15*105f6285SAndroid Build Coastguard Worker# the License.
16*105f6285SAndroid Build Coastguard Worker
17*105f6285SAndroid Build Coastguard Workerimport argparse
18*105f6285SAndroid Build Coastguard Workerimport dataclasses
19*105f6285SAndroid Build Coastguard Workerimport os
20*105f6285SAndroid Build Coastguard Workerimport shlex
21*105f6285SAndroid Build Coastguard Workerimport subprocess
22*105f6285SAndroid Build Coastguard Workerimport tempfile
23*105f6285SAndroid Build Coastguard Workerfrom typing import List
24*105f6285SAndroid Build Coastguard Worker
25*105f6285SAndroid Build Coastguard Workerfrom build_chd_utils import copy_files, unzip_otatools
26*105f6285SAndroid Build Coastguard Worker
27*105f6285SAndroid Build Coastguard Worker"""Builds a vendor_boot-chd_debug.img.
28*105f6285SAndroid Build Coastguard Worker
29*105f6285SAndroid Build Coastguard WorkerThe vendor_boot-chd_debug.img is built by adding those CHD specific debugging
30*105f6285SAndroid Build Coastguard Workerfiles to a Cuttlefish's vendor_boot-debug.img, using a new ramdisk fragment.
31*105f6285SAndroid Build Coastguard Worker
32*105f6285SAndroid Build Coastguard WorkerTest command:
33*105f6285SAndroid Build Coastguard Workerpython3 tools/treble/cuttlefish/build_chd_debug_ramdisk.py \
34*105f6285SAndroid Build Coastguard Worker    $ANDROID_PRODUCT_OUT/vendor_boot-debug.img \
35*105f6285SAndroid Build Coastguard Worker    -o $ANDROID_PRODUCT_OUT/vendor_boot-chd_debug.img \
36*105f6285SAndroid Build Coastguard Worker    --otatools_zip $ANDROID_PRODUCT_OUT/otatools.zip \
37*105f6285SAndroid Build Coastguard Worker    --add_file chd_debug.prop:adb_debug.prop
38*105f6285SAndroid Build Coastguard Worker"""
39*105f6285SAndroid Build Coastguard Worker
40*105f6285SAndroid Build Coastguard Worker# The value of ramdisk type needs to be synchronized with
41*105f6285SAndroid Build Coastguard Worker# `system/tools/mkbootimg/mkbootimg.py`. We choose `_PLATFORM` here because the
42*105f6285SAndroid Build Coastguard Worker# CHD debug ramdisk will be used in normal boot (not for _RECOVERY or _DLKM).
43*105f6285SAndroid Build Coastguard Worker_VENDOR_RAMDISK_TYPE_PLATFORM = '1'
44*105f6285SAndroid Build Coastguard Worker
45*105f6285SAndroid Build Coastguard Worker
46*105f6285SAndroid Build Coastguard Workerdef _parse_args() -> argparse.Namespace:
47*105f6285SAndroid Build Coastguard Worker  """Parse the arguments for building the chd debug ramdisk.
48*105f6285SAndroid Build Coastguard Worker
49*105f6285SAndroid Build Coastguard Worker  Returns:
50*105f6285SAndroid Build Coastguard Worker    An object of the parsed arguments.
51*105f6285SAndroid Build Coastguard Worker  """
52*105f6285SAndroid Build Coastguard Worker  parser = argparse.ArgumentParser()
53*105f6285SAndroid Build Coastguard Worker  parser.add_argument('input_img',
54*105f6285SAndroid Build Coastguard Worker                      help='The input Cuttlefish vendor boot debug image.')
55*105f6285SAndroid Build Coastguard Worker  parser.add_argument('--output_img', '-o', required=True,
56*105f6285SAndroid Build Coastguard Worker                      help='The output CHD vendor boot debug image.')
57*105f6285SAndroid Build Coastguard Worker  parser.add_argument('--otatools_zip', required=True,
58*105f6285SAndroid Build Coastguard Worker                      help='Path to the otatools.zip.')
59*105f6285SAndroid Build Coastguard Worker  parser.add_argument('--add_file', action='append', default=[],
60*105f6285SAndroid Build Coastguard Worker                      help='The file to be added to the CHD debug ramdisk. '
61*105f6285SAndroid Build Coastguard Worker                           'The format is <src path>:<dst path>.')
62*105f6285SAndroid Build Coastguard Worker  return parser.parse_args()
63*105f6285SAndroid Build Coastguard Worker
64*105f6285SAndroid Build Coastguard Worker
65*105f6285SAndroid Build Coastguard Worker@dataclasses.dataclass
66*105f6285SAndroid Build Coastguard Workerclass ImageOptions:
67*105f6285SAndroid Build Coastguard Worker  """The options for building the CHD vendor boot debug image.
68*105f6285SAndroid Build Coastguard Worker
69*105f6285SAndroid Build Coastguard Worker  Attributes:
70*105f6285SAndroid Build Coastguard Worker    input_image: path of the input vendor boot debug image.
71*105f6285SAndroid Build Coastguard Worker    output_image: path of the output CHD vendor boot debug image.
72*105f6285SAndroid Build Coastguard Worker    otatools_dir: path of the otatools directory.
73*105f6285SAndroid Build Coastguard Worker    temp_dir: path of the temporary directory for ramdisk filesystem.
74*105f6285SAndroid Build Coastguard Worker    files_to_add: a list of files to be added in the debug ramdisk, where a
75*105f6285SAndroid Build Coastguard Worker      pair defines the src and dst path of each file.
76*105f6285SAndroid Build Coastguard Worker    files_to_remove: a list of files to be removed from the input vendor boot
77*105f6285SAndroid Build Coastguard Worker      debug image.
78*105f6285SAndroid Build Coastguard Worker  """
79*105f6285SAndroid Build Coastguard Worker  input_image: str
80*105f6285SAndroid Build Coastguard Worker  output_image: str
81*105f6285SAndroid Build Coastguard Worker  otatools_dir: str
82*105f6285SAndroid Build Coastguard Worker  temp_dir: str
83*105f6285SAndroid Build Coastguard Worker  files_to_add: List[str] = dataclasses.field(default_factory=list)
84*105f6285SAndroid Build Coastguard Worker  files_to_remove: List[str] = dataclasses.field(default_factory=list)
85*105f6285SAndroid Build Coastguard Worker
86*105f6285SAndroid Build Coastguard Worker
87*105f6285SAndroid Build Coastguard Worker@dataclasses.dataclass
88*105f6285SAndroid Build Coastguard Workerclass BootImage:
89*105f6285SAndroid Build Coastguard Worker  """Provide some functions to modify a boot image.
90*105f6285SAndroid Build Coastguard Worker
91*105f6285SAndroid Build Coastguard Worker  Attributes:
92*105f6285SAndroid Build Coastguard Worker    bootimg: path of the input boot image to be modified.
93*105f6285SAndroid Build Coastguard Worker    bootimg_dir: path of a temporary directory that would be used to extract
94*105f6285SAndroid Build Coastguard Worker      the input boot image.
95*105f6285SAndroid Build Coastguard Worker    unpack_bootimg_bin: path of the `unpack_bootimg` executable.
96*105f6285SAndroid Build Coastguard Worker    mkbootfs_bin: path of the `mkbootfs` executable.
97*105f6285SAndroid Build Coastguard Worker    mkbootimg_bin: path of the `mkbootimg` executable.
98*105f6285SAndroid Build Coastguard Worker    lz4_bin: path of the `lz4` executable.
99*105f6285SAndroid Build Coastguard Worker    toybox_bin: path of the `toybox` executable.
100*105f6285SAndroid Build Coastguard Worker    bootimg_args: the arguments that were used to build this boot image.
101*105f6285SAndroid Build Coastguard Worker  """
102*105f6285SAndroid Build Coastguard Worker  bootimg: str
103*105f6285SAndroid Build Coastguard Worker  bootimg_dir: str
104*105f6285SAndroid Build Coastguard Worker  unpack_bootimg_bin: str
105*105f6285SAndroid Build Coastguard Worker  mkbootfs_bin: str
106*105f6285SAndroid Build Coastguard Worker  mkbootimg_bin: str
107*105f6285SAndroid Build Coastguard Worker  lz4_bin: str
108*105f6285SAndroid Build Coastguard Worker  toybox_bin: str
109*105f6285SAndroid Build Coastguard Worker  bootimg_args: List[str] = dataclasses.field(default_factory=list)
110*105f6285SAndroid Build Coastguard Worker
111*105f6285SAndroid Build Coastguard Worker  def _get_ramdisk_fragments(self) -> List[str]:
112*105f6285SAndroid Build Coastguard Worker    """Get the path to all ramdisk fragments at `self.bootimg_dir`."""
113*105f6285SAndroid Build Coastguard Worker    return [os.path.join(self.bootimg_dir, file)
114*105f6285SAndroid Build Coastguard Worker            for file in os.listdir(self.bootimg_dir)
115*105f6285SAndroid Build Coastguard Worker            if file.startswith('vendor_ramdisk')]
116*105f6285SAndroid Build Coastguard Worker
117*105f6285SAndroid Build Coastguard Worker  def _compress_ramdisk(self, root_dir: str, ramdisk_file: str) -> None:
118*105f6285SAndroid Build Coastguard Worker    """Compress all the files under `root_dir` to generate `ramdisk_file`.
119*105f6285SAndroid Build Coastguard Worker
120*105f6285SAndroid Build Coastguard Worker    Args:
121*105f6285SAndroid Build Coastguard Worker      root_dir: root directory of the ramdisk content.
122*105f6285SAndroid Build Coastguard Worker      ramdisk_file: path of the output ramdisk file.
123*105f6285SAndroid Build Coastguard Worker    """
124*105f6285SAndroid Build Coastguard Worker    mkbootfs_cmd = [self.mkbootfs_bin, root_dir]
125*105f6285SAndroid Build Coastguard Worker    mkbootfs_result = subprocess.run(
126*105f6285SAndroid Build Coastguard Worker        mkbootfs_cmd, check=True, capture_output=True)
127*105f6285SAndroid Build Coastguard Worker    compress_cmd = [self.lz4_bin, '-l', '-12', '--favor-decSpeed']
128*105f6285SAndroid Build Coastguard Worker    with open(ramdisk_file, 'w') as o:
129*105f6285SAndroid Build Coastguard Worker      subprocess.run(
130*105f6285SAndroid Build Coastguard Worker          compress_cmd, check=True, input=mkbootfs_result.stdout, stdout=o)
131*105f6285SAndroid Build Coastguard Worker
132*105f6285SAndroid Build Coastguard Worker  def _decompress_ramdisk(self, ramdisk_file: str, output_dir: str) -> str:
133*105f6285SAndroid Build Coastguard Worker    """Decompress `ramdisk_file` to a new file at `output_dir`.
134*105f6285SAndroid Build Coastguard Worker
135*105f6285SAndroid Build Coastguard Worker    Args:
136*105f6285SAndroid Build Coastguard Worker      ramdisk_file: path of the ramdisk file to be decompressed.
137*105f6285SAndroid Build Coastguard Worker      output_dir: path of the output directory.
138*105f6285SAndroid Build Coastguard Worker
139*105f6285SAndroid Build Coastguard Worker    Returns:
140*105f6285SAndroid Build Coastguard Worker      Path of the uncompressed ramdisk.
141*105f6285SAndroid Build Coastguard Worker    """
142*105f6285SAndroid Build Coastguard Worker    if not os.path.exists(output_dir):
143*105f6285SAndroid Build Coastguard Worker      raise FileNotFoundError(f'Decompress output {output_dir} does not exist')
144*105f6285SAndroid Build Coastguard Worker    uncompressed_ramdisk = os.path.join(output_dir, 'uncompressed_ramdisk')
145*105f6285SAndroid Build Coastguard Worker    decompress_cmd = [self.lz4_bin, '-d', ramdisk_file, uncompressed_ramdisk]
146*105f6285SAndroid Build Coastguard Worker    subprocess.run(decompress_cmd, check=True)
147*105f6285SAndroid Build Coastguard Worker    return uncompressed_ramdisk
148*105f6285SAndroid Build Coastguard Worker
149*105f6285SAndroid Build Coastguard Worker  def _extract_ramdisk(self, ramdisk_file: str, root_dir: str) -> None:
150*105f6285SAndroid Build Coastguard Worker    """Extract the files from a uncompressed ramdisk to `root_dir`.
151*105f6285SAndroid Build Coastguard Worker
152*105f6285SAndroid Build Coastguard Worker    Args:
153*105f6285SAndroid Build Coastguard Worker      ramdisk_file: path of the ramdisk file to be extracted.
154*105f6285SAndroid Build Coastguard Worker      root_dir: path of the extracted ramdisk root directory.
155*105f6285SAndroid Build Coastguard Worker    """
156*105f6285SAndroid Build Coastguard Worker    # Use `toybox cpio` instead of `cpio` to avoid invoking cpio from the host
157*105f6285SAndroid Build Coastguard Worker    # environment.
158*105f6285SAndroid Build Coastguard Worker    extract_cmd = [self.toybox_bin, 'cpio', '-i', '-F', ramdisk_file]
159*105f6285SAndroid Build Coastguard Worker    subprocess.run(extract_cmd, cwd=root_dir, check=True)
160*105f6285SAndroid Build Coastguard Worker
161*105f6285SAndroid Build Coastguard Worker  def unpack(self) -> None:
162*105f6285SAndroid Build Coastguard Worker    """Unpack the boot.img and capture the bootimg arguments."""
163*105f6285SAndroid Build Coastguard Worker    if self.bootimg_args:
164*105f6285SAndroid Build Coastguard Worker      raise RuntimeError(f'cannot unpack {self.bootimg} twice')
165*105f6285SAndroid Build Coastguard Worker    print(f'Unpacking {self.bootimg} to {self.bootimg_dir}')
166*105f6285SAndroid Build Coastguard Worker    unpack_cmd = [
167*105f6285SAndroid Build Coastguard Worker        self.unpack_bootimg_bin,
168*105f6285SAndroid Build Coastguard Worker        '--boot_img', self.bootimg,
169*105f6285SAndroid Build Coastguard Worker        '--out', self.bootimg_dir,
170*105f6285SAndroid Build Coastguard Worker        '--format', 'mkbootimg'
171*105f6285SAndroid Build Coastguard Worker    ]
172*105f6285SAndroid Build Coastguard Worker    unpack_result = subprocess.run(unpack_cmd, check=True,
173*105f6285SAndroid Build Coastguard Worker                                   capture_output=True, encoding='utf-8')
174*105f6285SAndroid Build Coastguard Worker    self.bootimg_args = shlex.split(unpack_result.stdout)
175*105f6285SAndroid Build Coastguard Worker
176*105f6285SAndroid Build Coastguard Worker  def add_ramdisk(self, ramdisk_root: str) -> None:
177*105f6285SAndroid Build Coastguard Worker    """Add a new ramdisk fragment and update the bootimg arguments.
178*105f6285SAndroid Build Coastguard Worker
179*105f6285SAndroid Build Coastguard Worker    Args:
180*105f6285SAndroid Build Coastguard Worker      ramdisk_root: path of the root directory which contains the content of
181*105f6285SAndroid Build Coastguard Worker        the new ramdisk fragment.
182*105f6285SAndroid Build Coastguard Worker    """
183*105f6285SAndroid Build Coastguard Worker    # Name the new ramdisk using the smallest unused index.
184*105f6285SAndroid Build Coastguard Worker    ramdisk_fragments = self._get_ramdisk_fragments()
185*105f6285SAndroid Build Coastguard Worker    new_ramdisk_name = f'vendor_ramdisk{len(ramdisk_fragments):02d}'
186*105f6285SAndroid Build Coastguard Worker    new_ramdisk_file = os.path.join(self.bootimg_dir, new_ramdisk_name)
187*105f6285SAndroid Build Coastguard Worker    if os.path.exists(new_ramdisk_file):
188*105f6285SAndroid Build Coastguard Worker      raise FileExistsError(f'{new_ramdisk_file} already exists')
189*105f6285SAndroid Build Coastguard Worker    print(f'Adding a new vendor ramdisk fragment {new_ramdisk_file}')
190*105f6285SAndroid Build Coastguard Worker    self._compress_ramdisk(ramdisk_root, new_ramdisk_file)
191*105f6285SAndroid Build Coastguard Worker
192*105f6285SAndroid Build Coastguard Worker    # Update the bootimg arguments to include the new ramdisk file.
193*105f6285SAndroid Build Coastguard Worker    self.bootimg_args.extend([
194*105f6285SAndroid Build Coastguard Worker        '--ramdisk_type', _VENDOR_RAMDISK_TYPE_PLATFORM,
195*105f6285SAndroid Build Coastguard Worker        '--ramdisk_name', 'chd',
196*105f6285SAndroid Build Coastguard Worker        '--vendor_ramdisk_fragment', new_ramdisk_file
197*105f6285SAndroid Build Coastguard Worker    ])
198*105f6285SAndroid Build Coastguard Worker
199*105f6285SAndroid Build Coastguard Worker  def remove_file(self, file_name: str) -> None:
200*105f6285SAndroid Build Coastguard Worker    """Remove `file_name` from all the existing ramdisk fragments.
201*105f6285SAndroid Build Coastguard Worker
202*105f6285SAndroid Build Coastguard Worker    Args:
203*105f6285SAndroid Build Coastguard Worker      file_name: path of the file to be removed, relative to the ramdisk root
204*105f6285SAndroid Build Coastguard Worker        directory.
205*105f6285SAndroid Build Coastguard Worker
206*105f6285SAndroid Build Coastguard Worker    Raises:
207*105f6285SAndroid Build Coastguard Worker      FileNotFoundError if `file_name` cannot be found in any of the ramdisk
208*105f6285SAndroid Build Coastguard Worker        fragments.
209*105f6285SAndroid Build Coastguard Worker    """
210*105f6285SAndroid Build Coastguard Worker    ramdisk_fragments = self._get_ramdisk_fragments()
211*105f6285SAndroid Build Coastguard Worker    is_removed = False
212*105f6285SAndroid Build Coastguard Worker    for ramdisk in ramdisk_fragments:
213*105f6285SAndroid Build Coastguard Worker      print(f'Attempting to remove {file_name} from {ramdisk}')
214*105f6285SAndroid Build Coastguard Worker      with tempfile.TemporaryDirectory() as temp_dir:
215*105f6285SAndroid Build Coastguard Worker        uncompressed_ramdisk = self._decompress_ramdisk(ramdisk, temp_dir)
216*105f6285SAndroid Build Coastguard Worker        extracted_ramdisk_dir = os.path.join(temp_dir, 'extracted_ramdisk')
217*105f6285SAndroid Build Coastguard Worker        os.mkdir(extracted_ramdisk_dir)
218*105f6285SAndroid Build Coastguard Worker        self._extract_ramdisk(uncompressed_ramdisk, extracted_ramdisk_dir)
219*105f6285SAndroid Build Coastguard Worker        file_path = os.path.join(extracted_ramdisk_dir, file_name)
220*105f6285SAndroid Build Coastguard Worker        if os.path.exists(file_path):
221*105f6285SAndroid Build Coastguard Worker          os.remove(file_path)
222*105f6285SAndroid Build Coastguard Worker          is_removed = True
223*105f6285SAndroid Build Coastguard Worker          print(f'{file_path} was removed')
224*105f6285SAndroid Build Coastguard Worker        self._compress_ramdisk(extracted_ramdisk_dir, ramdisk)
225*105f6285SAndroid Build Coastguard Worker
226*105f6285SAndroid Build Coastguard Worker    if not is_removed:
227*105f6285SAndroid Build Coastguard Worker      raise FileNotFoundError(
228*105f6285SAndroid Build Coastguard Worker          f'cannot remove {file_name} from {ramdisk_fragments}'
229*105f6285SAndroid Build Coastguard Worker      )
230*105f6285SAndroid Build Coastguard Worker
231*105f6285SAndroid Build Coastguard Worker  def pack(self, output_img: str) -> None:
232*105f6285SAndroid Build Coastguard Worker    """Pack the boot.img using `self.bootimg_args`.
233*105f6285SAndroid Build Coastguard Worker
234*105f6285SAndroid Build Coastguard Worker    Args:
235*105f6285SAndroid Build Coastguard Worker      output_img: path of the output boot image.
236*105f6285SAndroid Build Coastguard Worker    """
237*105f6285SAndroid Build Coastguard Worker    print(f'Packing {output_img} with args: {self.bootimg_args}')
238*105f6285SAndroid Build Coastguard Worker    mkbootimg_cmd = [
239*105f6285SAndroid Build Coastguard Worker        self.mkbootimg_bin, '--vendor_boot', output_img
240*105f6285SAndroid Build Coastguard Worker    ] + self.bootimg_args
241*105f6285SAndroid Build Coastguard Worker    subprocess.check_call(mkbootimg_cmd)
242*105f6285SAndroid Build Coastguard Worker
243*105f6285SAndroid Build Coastguard Worker
244*105f6285SAndroid Build Coastguard Workerdef _prepare_env(otatools_dir: str) -> List[str]:
245*105f6285SAndroid Build Coastguard Worker  """Get the executable path of the required otatools.
246*105f6285SAndroid Build Coastguard Worker
247*105f6285SAndroid Build Coastguard Worker  We need `unpack_bootimg`, `mkbootfs`, `mkbootimg`, `lz4` and `toybox` for
248*105f6285SAndroid Build Coastguard Worker  building CHD debug ramdisk. This function returns the path to the above tools
249*105f6285SAndroid Build Coastguard Worker  in order.
250*105f6285SAndroid Build Coastguard Worker
251*105f6285SAndroid Build Coastguard Worker  Args:
252*105f6285SAndroid Build Coastguard Worker    otatools_dir: path of the otatools directory.
253*105f6285SAndroid Build Coastguard Worker
254*105f6285SAndroid Build Coastguard Worker  Raises:
255*105f6285SAndroid Build Coastguard Worker    FileNotFoundError if any required otatool does not exist.
256*105f6285SAndroid Build Coastguard Worker  """
257*105f6285SAndroid Build Coastguard Worker  tools_path = []
258*105f6285SAndroid Build Coastguard Worker  for tool in ['unpack_bootimg', 'mkbootfs', 'mkbootimg', 'lz4', 'toybox']:
259*105f6285SAndroid Build Coastguard Worker    tool_path = os.path.join(otatools_dir, 'bin', tool)
260*105f6285SAndroid Build Coastguard Worker    if not os.path.exists(tool_path):
261*105f6285SAndroid Build Coastguard Worker      raise FileNotFoundError(f'otatool {tool_path} does not exist')
262*105f6285SAndroid Build Coastguard Worker    tools_path.append(tool_path)
263*105f6285SAndroid Build Coastguard Worker  return tools_path
264*105f6285SAndroid Build Coastguard Worker
265*105f6285SAndroid Build Coastguard Worker
266*105f6285SAndroid Build Coastguard Workerdef build_chd_debug_ramdisk(options: ImageOptions) -> None:
267*105f6285SAndroid Build Coastguard Worker  """Build a new vendor boot debug image.
268*105f6285SAndroid Build Coastguard Worker
269*105f6285SAndroid Build Coastguard Worker  1. If `options.files_to_remove` present, remove these files from all the
270*105f6285SAndroid Build Coastguard Worker     existing ramdisk fragments.
271*105f6285SAndroid Build Coastguard Worker  2. If `options.files_to_add` present, create a new ramdisk fragment which
272*105f6285SAndroid Build Coastguard Worker     adds these files, and add this new fragment into the input image.
273*105f6285SAndroid Build Coastguard Worker
274*105f6285SAndroid Build Coastguard Worker  Args:
275*105f6285SAndroid Build Coastguard Worker    options: a `ImageOptions` object which specifies the options for building
276*105f6285SAndroid Build Coastguard Worker      a CHD vendor boot debug image.
277*105f6285SAndroid Build Coastguard Worker
278*105f6285SAndroid Build Coastguard Worker  Raises:
279*105f6285SAndroid Build Coastguard Worker    FileExistsError if having duplicated ramdisk fragments.
280*105f6285SAndroid Build Coastguard Worker    FileNotFoundError if any required otatool does not exist or if
281*105f6285SAndroid Build Coastguard Worker      `options.files_to_remove` is not present in any of the ramdisk fragments
282*105f6285SAndroid Build Coastguard Worker      of `input_image`.
283*105f6285SAndroid Build Coastguard Worker  """
284*105f6285SAndroid Build Coastguard Worker  unpack_bootimg, mkbootfs, mkbootimg, lz4, toybox = _prepare_env(
285*105f6285SAndroid Build Coastguard Worker      options.otatools_dir)
286*105f6285SAndroid Build Coastguard Worker  bootimg = BootImage(
287*105f6285SAndroid Build Coastguard Worker      bootimg=options.input_image,
288*105f6285SAndroid Build Coastguard Worker      bootimg_dir=os.path.join(options.temp_dir, 'bootimg'),
289*105f6285SAndroid Build Coastguard Worker      unpack_bootimg_bin=unpack_bootimg,
290*105f6285SAndroid Build Coastguard Worker      mkbootfs_bin=mkbootfs,
291*105f6285SAndroid Build Coastguard Worker      mkbootimg_bin=mkbootimg,
292*105f6285SAndroid Build Coastguard Worker      lz4_bin=lz4,
293*105f6285SAndroid Build Coastguard Worker      toybox_bin=toybox)
294*105f6285SAndroid Build Coastguard Worker  bootimg.unpack()
295*105f6285SAndroid Build Coastguard Worker
296*105f6285SAndroid Build Coastguard Worker  for f in options.files_to_remove:
297*105f6285SAndroid Build Coastguard Worker    bootimg.remove_file(f)
298*105f6285SAndroid Build Coastguard Worker
299*105f6285SAndroid Build Coastguard Worker  if options.files_to_add:
300*105f6285SAndroid Build Coastguard Worker    print(f'Adding {options.files_to_add} to {options.input_image}')
301*105f6285SAndroid Build Coastguard Worker    new_ramdisk_fragment = os.path.join(options.temp_dir,
302*105f6285SAndroid Build Coastguard Worker                                        'new_ramdisk_fragment')
303*105f6285SAndroid Build Coastguard Worker    os.mkdir(new_ramdisk_fragment)
304*105f6285SAndroid Build Coastguard Worker    copy_files(options.files_to_add, new_ramdisk_fragment)
305*105f6285SAndroid Build Coastguard Worker    bootimg.add_ramdisk(new_ramdisk_fragment)
306*105f6285SAndroid Build Coastguard Worker
307*105f6285SAndroid Build Coastguard Worker  bootimg.pack(options.output_image)
308*105f6285SAndroid Build Coastguard Worker
309*105f6285SAndroid Build Coastguard Worker
310*105f6285SAndroid Build Coastguard Workerdef main(temp_dir: str) -> None:
311*105f6285SAndroid Build Coastguard Worker  args = _parse_args()
312*105f6285SAndroid Build Coastguard Worker  otatools_dir = os.path.join(temp_dir, 'otatools')
313*105f6285SAndroid Build Coastguard Worker  unzip_otatools(args.otatools_zip, otatools_dir, [
314*105f6285SAndroid Build Coastguard Worker      'bin/unpack_bootimg', 'bin/mkbootfs', 'bin/mkbootimg', 'bin/lz4',
315*105f6285SAndroid Build Coastguard Worker      'bin/toybox', 'lib64/*'
316*105f6285SAndroid Build Coastguard Worker  ])
317*105f6285SAndroid Build Coastguard Worker  options = ImageOptions(
318*105f6285SAndroid Build Coastguard Worker      input_image=args.input_img,
319*105f6285SAndroid Build Coastguard Worker      output_image=args.output_img,
320*105f6285SAndroid Build Coastguard Worker      otatools_dir=otatools_dir,
321*105f6285SAndroid Build Coastguard Worker      temp_dir=temp_dir,
322*105f6285SAndroid Build Coastguard Worker      files_to_add=args.add_file)
323*105f6285SAndroid Build Coastguard Worker  build_chd_debug_ramdisk(options)
324*105f6285SAndroid Build Coastguard Worker
325*105f6285SAndroid Build Coastguard Worker
326*105f6285SAndroid Build Coastguard Workerif __name__ == '__main__':
327*105f6285SAndroid Build Coastguard Worker  with tempfile.TemporaryDirectory() as temp_dir:
328*105f6285SAndroid Build Coastguard Worker    main(temp_dir)
329