blob: 8cd04cfd895de332ec8acb5d67184e00c61f852b [file] [log] [blame]
Ram Chandrasekar913c0422022-05-09 23:41:22 +00001# Copyright 2022 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""build_image is used to build a Chromium OS image.
6
7Chromium OS comes in many different forms. This script can be used to build
8the following:
9
10base - Pristine Chromium OS image. As similar to Chrome OS as possible.
11dev [default] - Developer image. Like base but with additional dev packages.
12test - Like dev, but with additional test specific packages and can be easily
13 used for automated testing using scripts like test_that, etc.
14factory_install - Install shim for bootstrapping the factory test process.
15 Cannot be built along with any other image.
16
17Examples:
18
19build_image --board=<board> dev test - builds developer and test images.
20build_image --board=<board> factory_install - builds a factory install shim.
21
22Note if you want to build an image with custom size partitions, either consider
23adding a new disk layout in build_library/legacy_disk_layout.json OR use
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000024adjust-part. Here are a few examples:
Ram Chandrasekar913c0422022-05-09 23:41:22 +000025
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000026adjust-part='STATE:+1G' -- add one GB to the size the stateful partition
27adjust-part='ROOT-A:-1G' -- remove one GB from the primary rootfs partition
28adjust-part='STATE:=1G' -- make the stateful partition 1 GB
Ram Chandrasekar913c0422022-05-09 23:41:22 +000029"""
30
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000031import argparse
32import os
Ram Chandrasekar913c0422022-05-09 23:41:22 +000033from pathlib import Path
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000034from typing import List, Optional, Tuple
Ram Chandrasekar913c0422022-05-09 23:41:22 +000035
36from chromite.lib import commandline
37from chromite.lib import constants
38from chromite.lib import cros_build_lib
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000039from chromite.service import image
Ram Chandrasekar158b32a2022-07-15 17:06:13 +000040from chromite.utils import timer
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000041
42
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000043def build_shell_bool_style_args(
44 parser: commandline.ArgumentParser,
45 name: str,
46 default_val: bool,
47 help_str: str,
48 deprecation_note: str,
49 alternate_name: Optional[str] = None,
50 additional_neg_options: Optional[List[str]] = None) -> None:
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000051 """Build the shell boolean input argument equivalent.
52
53 There are two cases which we will need to handle,
54 case 1: A shell boolean arg, which doesn't need to be re-worded in python.
55 case 2: A shell boolean arg, which needs to be re-worded in python.
56 Example below.
57 For Case 1, for a given input arg name 'argA', we create three python
58 arguments.
59 --argA, --noargA, --no-argA. The arguments --argA and --no-argA will be
60 retained after deprecating --noargA.
61 For Case 2, for a given input arg name 'arg_A' we need to use alternate
62 argument name 'arg-A'. we create four python arguments in this case.
63 --arg_A, --noarg_A, --arg-A, --no-arg-A. The first two arguments will be
64 deprecated later.
65 TODO(b/232566937): Remove the creation of --noargA in case 1 and --arg_A and
66 --noarg_A in case 2.
67
68 Args:
69 parser: The parser to update.
70 name: The input argument name. This will be used as 'dest' variable name.
71 default_val: The default value to assign.
72 help_str: The help string for the input argument.
73 deprecation_note: A deprecation note to use.
74 alternate_name: Alternate argument to be used after deprecation.
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000075 additional_neg_options: Additional negative alias options to use.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000076 """
77 arg = f'--{name}'
78 shell_narg = f'--no{name}'
79 py_narg = f'--no-{name}'
80 alt_arg = f'--{alternate_name}' if alternate_name else None
81 alt_py_narg = f'--no-{alternate_name}' if alternate_name else None
82 default_val_str = f'{help_str} (Default: %(default)s).'
83
84 if alternate_name:
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000085 _alternate_narg_list = [alt_py_narg]
86 if additional_neg_options:
87 _alternate_narg_list.extend(additional_neg_options)
88
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000089 parser.add_argument(
90 alt_arg,
91 action='store_true',
92 default=default_val,
93 dest=name,
94 help=default_val_str)
95 parser.add_argument(
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000096 *_alternate_narg_list,
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000097 action='store_false',
98 dest=name,
99 help="Don't " + help_str.lower())
100
101 parser.add_argument(
102 arg,
103 action='store_true',
104 default=default_val,
105 dest=name,
106 deprecated=deprecation_note % alt_arg if alternate_name else None,
107 help=default_val_str if not alternate_name else argparse.SUPPRESS)
108 parser.add_argument(
109 shell_narg,
110 action='store_false',
111 dest=name,
112 deprecated=deprecation_note %
113 (alt_py_narg if alternate_name else py_narg),
114 help=argparse.SUPPRESS)
115
116 if not alternate_name:
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000117 _py_narg_list = [py_narg]
118 if additional_neg_options:
119 _py_narg_list.extend(additional_neg_options)
120
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000121 parser.add_argument(
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000122 *_py_narg_list,
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000123 action='store_false',
124 dest=name,
125 help="Don't " + help_str.lower())
126
127
128def build_shell_string_style_args(parser: commandline.ArgumentParser, name: str,
129 default_val: Optional[str], help_str: str,
130 deprecation_note: str,
131 alternate_name: str) -> None:
132 """Build the shell string input argument equivalent.
133
134 Args:
135 parser: The parser to update.
136 name: The input argument name. This will be used as 'dest' variable name.
137 default_val: The default value to assign.
138 help_str: The help string for the input argument.
139 deprecation_note: A deprecation note to use.
140 alternate_name: Alternate argument to be used after deprecation.
141 """
142 default_val_str = (f'{help_str} (Default: %(default)s).'
143 if default_val else help_str)
144
145 parser.add_argument(
146 f'--{alternate_name}',
147 dest=f'{name}',
148 default=default_val,
149 help=default_val_str)
150 parser.add_argument(
151 f'--{name}',
152 deprecated=deprecation_note % f'--{alternate_name}',
153 help=argparse.SUPPRESS)
154
155
156def get_parser() -> commandline.ArgumentParser:
157 """Creates the cmdline argparser, populates the options and description.
158
159 Returns:
160 Argument parser.
161 """
162 deprecation_note = 'Argument will be removed January, 2023. Use %s instead.'
163 parser = commandline.ArgumentParser(description=__doc__)
164
165 parser.add_argument(
166 '-b',
167 '--board',
168 '--build-target',
169 dest='board',
170 default=cros_build_lib.GetDefaultBoard(),
171 help='The board to build images for.')
172 build_shell_string_style_args(
173 parser, 'adjust_part', None,
174 'Adjustments to apply to partition table (LABEL:[+-=]SIZE) '
175 'e.g. ROOT-A:+1G.', deprecation_note, 'adjust-partition')
176 build_shell_string_style_args(
177 parser, 'output_root',
178 Path(constants.DEFAULT_BUILD_ROOT) / 'images',
179 'Directory in which to place image result directories '
180 '(named by version).', deprecation_note, 'output-root')
181 build_shell_string_style_args(
182 parser, 'builder_path', None,
183 'The build name to be installed on DUT during hwtest.', deprecation_note,
184 'builder-path')
185 build_shell_string_style_args(parser, 'disk_layout', 'default',
186 'The disk layout type to use for this image.',
187 deprecation_note, 'disk-layout')
188
189 # Kernel related options.
190 group = parser.add_argument_group('Kernel Options')
191 build_shell_string_style_args(
192 group, 'enable_serial', None,
193 'Enable serial port for printks. Example values: ttyS0.',
194 deprecation_note, 'enable-serial')
195 group.add_argument(
196 '--kernel-loglevel',
197 type=int,
198 default=7,
199 help='The loglevel to add to the kernel command line. '
200 '(Default: %(default)s).')
201 group.add_argument(
202 '--loglevel',
203 dest='kernel_loglevel',
204 type=int,
205 deprecated=deprecation_note % 'kernel-loglevel',
206 help=argparse.SUPPRESS)
207
208 # Bootloader related options.
209 group = parser.add_argument_group('Bootloader Options')
210 build_shell_string_style_args(
211 group, 'boot_args', 'noinitrd',
212 'Additional boot arguments to pass to the commandline.', deprecation_note,
213 'boot-args')
214 build_shell_bool_style_args(group, 'enable_bootcache', False,
215 'Make all bootloaders to use boot cache.',
216 deprecation_note, 'enable-bootcache')
217 build_shell_bool_style_args(
218 group, 'enable_rootfs_verification', True,
219 'Make all bootloaders to use kernel based root-fs integrity checking.',
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000220 deprecation_note, 'enable-rootfs-verification', ['-r'])
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000221
222 # Advanced options.
223 group = parser.add_argument_group('Advanced Options')
224 group.add_argument(
225 '--build-attempt',
226 type=int,
227 default=1,
228 help='The build attempt for this image build. (Default: %(default)s).')
229 group.add_argument(
230 '--build_attempt',
231 type=int,
232 deprecated=deprecation_note % 'build-attempt',
233 help=argparse.SUPPRESS)
234 build_shell_string_style_args(
235 group, 'build_root',
236 Path(constants.DEFAULT_BUILD_ROOT) / 'images',
237 'Directory in which to compose the image, before copying it to '
238 'output_root.', deprecation_note, 'build-root')
239 group.add_argument(
240 '-j',
241 '--jobs',
242 dest='jobs',
243 type=int,
244 default=os.cpu_count(),
245 help='Number of packages to build in parallel at maximum. '
246 '(Default: %(default)s).')
247 build_shell_bool_style_args(group, 'replace', False,
248 'Overwrite existing output, if any.',
249 deprecation_note)
250 group.add_argument(
251 '--symlink',
252 default='latest',
253 help='Symlink name to use for this image. (Default: %(default)s).')
254 group.add_argument(
255 '--version',
256 default=None,
257 help='Overrides version number in name to this version.')
258 build_shell_string_style_args(group, 'output_suffix', None,
259 'Add custom suffix to output directory.',
260 deprecation_note, 'output-suffix')
Ram Chandrasekar88157352022-06-29 18:15:28 +0000261 group.add_argument(
262 '--eclean',
263 action='store_true',
264 default=True,
265 dest='eclean',
266 deprecated=('eclean is being removed from build_images. Argument will be '
267 'removed January, 2023.'),
268 help=argparse.SUPPRESS)
269 group.add_argument(
270 '--noeclean',
271 action='store_false',
272 dest='eclean',
273 deprecated=('eclean is being removed from build_images. Argument will be '
274 'removed January, 2023.'),
275 help=argparse.SUPPRESS)
276 group.add_argument(
277 '--no-eclean',
278 action='store_false',
279 dest='eclean',
280 deprecated=('eclean is being removed from build_images. Argument will be '
281 'removed January, 2023.'),
282 help=argparse.SUPPRESS)
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000283
284 parser.add_argument(
285 'images',
286 nargs='*',
287 default=['dev'],
288 help='list of images to build. (Default: %(default)s).')
289
290 return parser
291
292
293def parse_args(
294 argv: List[str]
295) -> Tuple[commandline.ArgumentParser, commandline.ArgumentNamespace]:
296 """Parse and validate CLI arguments.
297
298 Args:
299 argv: Arguments passed via CLI.
300
301 Returns:
302 Tuple having the below two,
303 Argument Parser
304 Validated argument namespace.
305 """
306 parser = get_parser()
307 opts = parser.parse_args(argv)
308
309 opts.build_run_config = image.BuildConfig(
310 adjust_partition=opts.adjust_part,
311 output_root=opts.output_root,
312 builder_path=opts.builder_path,
313 disk_layout=opts.disk_layout,
314 enable_serial=opts.enable_serial,
315 kernel_loglevel=opts.kernel_loglevel,
316 boot_args=opts.boot_args,
317 enable_bootcache=opts.enable_bootcache,
318 enable_rootfs_verification=opts.enable_rootfs_verification,
319 build_attempt=opts.build_attempt,
320 build_root=opts.build_root,
321 jobs=opts.jobs,
322 replace=opts.replace,
323 symlink=opts.symlink,
324 version=opts.version,
325 output_dir_suffix=opts.output_suffix,
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000326 )
327 opts.Freeze()
328
329 return parser, opts
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000330
331
Ram Chandrasekar158b32a2022-07-15 17:06:13 +0000332@timer.timed('Elapsed time (build_image)')
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000333def main(argv: Optional[List[str]] = None) -> Optional[int]:
334 commandline.RunInsideChroot()
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000335 parser, opts = parse_args(argv)
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000336
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000337 # If the opts.board is not set, then it means user hasn't specified a default
338 # board in 'src/scripts/.default_board' and didn't specify it as input
339 # argument.
340 if not opts.board:
341 parser.error('--board is required')
342
343 invalid_image = [
344 x for x in opts.images if x not in constants.IMAGE_TYPE_TO_NAME
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000345 ]
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000346 if invalid_image:
347 parser.error(f'Invalid image type argument(s) {invalid_image}')
348
349 result = image.Build(opts.board, opts.images, opts.build_run_config)
350 if result.run_error:
351 cros_build_lib.Die(
352 f'Error running build_image. Exit Code : {result.return_code}')