blob: dc2ac910801d06eccbb85760cbc504d00d6adcef [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
40
41
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000042def build_shell_bool_style_args(
43 parser: commandline.ArgumentParser,
44 name: str,
45 default_val: bool,
46 help_str: str,
47 deprecation_note: str,
48 alternate_name: Optional[str] = None,
49 additional_neg_options: Optional[List[str]] = None) -> None:
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000050 """Build the shell boolean input argument equivalent.
51
52 There are two cases which we will need to handle,
53 case 1: A shell boolean arg, which doesn't need to be re-worded in python.
54 case 2: A shell boolean arg, which needs to be re-worded in python.
55 Example below.
56 For Case 1, for a given input arg name 'argA', we create three python
57 arguments.
58 --argA, --noargA, --no-argA. The arguments --argA and --no-argA will be
59 retained after deprecating --noargA.
60 For Case 2, for a given input arg name 'arg_A' we need to use alternate
61 argument name 'arg-A'. we create four python arguments in this case.
62 --arg_A, --noarg_A, --arg-A, --no-arg-A. The first two arguments will be
63 deprecated later.
64 TODO(b/232566937): Remove the creation of --noargA in case 1 and --arg_A and
65 --noarg_A in case 2.
66
67 Args:
68 parser: The parser to update.
69 name: The input argument name. This will be used as 'dest' variable name.
70 default_val: The default value to assign.
71 help_str: The help string for the input argument.
72 deprecation_note: A deprecation note to use.
73 alternate_name: Alternate argument to be used after deprecation.
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000074 additional_neg_options: Additional negative alias options to use.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000075 """
76 arg = f'--{name}'
77 shell_narg = f'--no{name}'
78 py_narg = f'--no-{name}'
79 alt_arg = f'--{alternate_name}' if alternate_name else None
80 alt_py_narg = f'--no-{alternate_name}' if alternate_name else None
81 default_val_str = f'{help_str} (Default: %(default)s).'
82
83 if alternate_name:
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000084 _alternate_narg_list = [alt_py_narg]
85 if additional_neg_options:
86 _alternate_narg_list.extend(additional_neg_options)
87
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000088 parser.add_argument(
89 alt_arg,
90 action='store_true',
91 default=default_val,
92 dest=name,
93 help=default_val_str)
94 parser.add_argument(
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000095 *_alternate_narg_list,
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000096 action='store_false',
97 dest=name,
98 help="Don't " + help_str.lower())
99
100 parser.add_argument(
101 arg,
102 action='store_true',
103 default=default_val,
104 dest=name,
105 deprecated=deprecation_note % alt_arg if alternate_name else None,
106 help=default_val_str if not alternate_name else argparse.SUPPRESS)
107 parser.add_argument(
108 shell_narg,
109 action='store_false',
110 dest=name,
111 deprecated=deprecation_note %
112 (alt_py_narg if alternate_name else py_narg),
113 help=argparse.SUPPRESS)
114
115 if not alternate_name:
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000116 _py_narg_list = [py_narg]
117 if additional_neg_options:
118 _py_narg_list.extend(additional_neg_options)
119
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000120 parser.add_argument(
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000121 *_py_narg_list,
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000122 action='store_false',
123 dest=name,
124 help="Don't " + help_str.lower())
125
126
127def build_shell_string_style_args(parser: commandline.ArgumentParser, name: str,
128 default_val: Optional[str], help_str: str,
129 deprecation_note: str,
130 alternate_name: str) -> None:
131 """Build the shell string input argument equivalent.
132
133 Args:
134 parser: The parser to update.
135 name: The input argument name. This will be used as 'dest' variable name.
136 default_val: The default value to assign.
137 help_str: The help string for the input argument.
138 deprecation_note: A deprecation note to use.
139 alternate_name: Alternate argument to be used after deprecation.
140 """
141 default_val_str = (f'{help_str} (Default: %(default)s).'
142 if default_val else help_str)
143
144 parser.add_argument(
145 f'--{alternate_name}',
146 dest=f'{name}',
147 default=default_val,
148 help=default_val_str)
149 parser.add_argument(
150 f'--{name}',
151 deprecated=deprecation_note % f'--{alternate_name}',
152 help=argparse.SUPPRESS)
153
154
155def get_parser() -> commandline.ArgumentParser:
156 """Creates the cmdline argparser, populates the options and description.
157
158 Returns:
159 Argument parser.
160 """
161 deprecation_note = 'Argument will be removed January, 2023. Use %s instead.'
162 parser = commandline.ArgumentParser(description=__doc__)
163
164 parser.add_argument(
165 '-b',
166 '--board',
167 '--build-target',
168 dest='board',
169 default=cros_build_lib.GetDefaultBoard(),
170 help='The board to build images for.')
171 build_shell_string_style_args(
172 parser, 'adjust_part', None,
173 'Adjustments to apply to partition table (LABEL:[+-=]SIZE) '
174 'e.g. ROOT-A:+1G.', deprecation_note, 'adjust-partition')
175 build_shell_string_style_args(
176 parser, 'output_root',
177 Path(constants.DEFAULT_BUILD_ROOT) / 'images',
178 'Directory in which to place image result directories '
179 '(named by version).', deprecation_note, 'output-root')
180 build_shell_string_style_args(
181 parser, 'builder_path', None,
182 'The build name to be installed on DUT during hwtest.', deprecation_note,
183 'builder-path')
184 build_shell_string_style_args(parser, 'disk_layout', 'default',
185 'The disk layout type to use for this image.',
186 deprecation_note, 'disk-layout')
187
188 # Kernel related options.
189 group = parser.add_argument_group('Kernel Options')
190 build_shell_string_style_args(
191 group, 'enable_serial', None,
192 'Enable serial port for printks. Example values: ttyS0.',
193 deprecation_note, 'enable-serial')
194 group.add_argument(
195 '--kernel-loglevel',
196 type=int,
197 default=7,
198 help='The loglevel to add to the kernel command line. '
199 '(Default: %(default)s).')
200 group.add_argument(
201 '--loglevel',
202 dest='kernel_loglevel',
203 type=int,
204 deprecated=deprecation_note % 'kernel-loglevel',
205 help=argparse.SUPPRESS)
206
207 # Bootloader related options.
208 group = parser.add_argument_group('Bootloader Options')
209 build_shell_string_style_args(
210 group, 'boot_args', 'noinitrd',
211 'Additional boot arguments to pass to the commandline.', deprecation_note,
212 'boot-args')
213 build_shell_bool_style_args(group, 'enable_bootcache', False,
214 'Make all bootloaders to use boot cache.',
215 deprecation_note, 'enable-bootcache')
216 build_shell_bool_style_args(
217 group, 'enable_rootfs_verification', True,
218 'Make all bootloaders to use kernel based root-fs integrity checking.',
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000219 deprecation_note, 'enable-rootfs-verification', ['-r'])
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000220
221 # Advanced options.
222 group = parser.add_argument_group('Advanced Options')
223 group.add_argument(
224 '--build-attempt',
225 type=int,
226 default=1,
227 help='The build attempt for this image build. (Default: %(default)s).')
228 group.add_argument(
229 '--build_attempt',
230 type=int,
231 deprecated=deprecation_note % 'build-attempt',
232 help=argparse.SUPPRESS)
233 build_shell_string_style_args(
234 group, 'build_root',
235 Path(constants.DEFAULT_BUILD_ROOT) / 'images',
236 'Directory in which to compose the image, before copying it to '
237 'output_root.', deprecation_note, 'build-root')
238 group.add_argument(
239 '-j',
240 '--jobs',
241 dest='jobs',
242 type=int,
243 default=os.cpu_count(),
244 help='Number of packages to build in parallel at maximum. '
245 '(Default: %(default)s).')
246 build_shell_bool_style_args(group, 'replace', False,
247 'Overwrite existing output, if any.',
248 deprecation_note)
249 group.add_argument(
250 '--symlink',
251 default='latest',
252 help='Symlink name to use for this image. (Default: %(default)s).')
253 group.add_argument(
254 '--version',
255 default=None,
256 help='Overrides version number in name to this version.')
257 build_shell_string_style_args(group, 'output_suffix', None,
258 'Add custom suffix to output directory.',
259 deprecation_note, 'output-suffix')
260 build_shell_bool_style_args(group, 'eclean', True,
261 'Call eclean before building the image.',
262 deprecation_note)
263
264 parser.add_argument(
265 'images',
266 nargs='*',
267 default=['dev'],
268 help='list of images to build. (Default: %(default)s).')
269
270 return parser
271
272
273def parse_args(
274 argv: List[str]
275) -> Tuple[commandline.ArgumentParser, commandline.ArgumentNamespace]:
276 """Parse and validate CLI arguments.
277
278 Args:
279 argv: Arguments passed via CLI.
280
281 Returns:
282 Tuple having the below two,
283 Argument Parser
284 Validated argument namespace.
285 """
286 parser = get_parser()
287 opts = parser.parse_args(argv)
288
289 opts.build_run_config = image.BuildConfig(
290 adjust_partition=opts.adjust_part,
291 output_root=opts.output_root,
292 builder_path=opts.builder_path,
293 disk_layout=opts.disk_layout,
294 enable_serial=opts.enable_serial,
295 kernel_loglevel=opts.kernel_loglevel,
296 boot_args=opts.boot_args,
297 enable_bootcache=opts.enable_bootcache,
298 enable_rootfs_verification=opts.enable_rootfs_verification,
299 build_attempt=opts.build_attempt,
300 build_root=opts.build_root,
301 jobs=opts.jobs,
302 replace=opts.replace,
303 symlink=opts.symlink,
304 version=opts.version,
305 output_dir_suffix=opts.output_suffix,
306 eclean=opts.eclean,
307 )
308 opts.Freeze()
309
310 return parser, opts
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000311
312
313def main(argv: Optional[List[str]] = None) -> Optional[int]:
314 commandline.RunInsideChroot()
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000315 parser, opts = parse_args(argv)
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000316
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000317 # If the opts.board is not set, then it means user hasn't specified a default
318 # board in 'src/scripts/.default_board' and didn't specify it as input
319 # argument.
320 if not opts.board:
321 parser.error('--board is required')
322
323 invalid_image = [
324 x for x in opts.images if x not in constants.IMAGE_TYPE_TO_NAME
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000325 ]
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000326 if invalid_image:
327 parser.error(f'Invalid image type argument(s) {invalid_image}')
328
329 result = image.Build(opts.board, opts.images, opts.build_run_config)
330 if result.run_error:
331 cros_build_lib.Die(
332 f'Error running build_image. Exit Code : {result.return_code}')