blob: 0324316e3c33a8f711c2bbe8c1e5af33fa47f2ef [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2022 The ChromiumOS Authors
Ram Chandrasekar913c0422022-05-09 23:41:22 +00002# 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,
Alex Klein1699fab2022-09-08 08:46:06 -060050 additional_neg_options: Optional[List[str]] = None,
51) -> None:
52 """Build the shell boolean input argument equivalent.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000053
Alex Klein1699fab2022-09-08 08:46:06 -060054 There are two cases which we will need to handle,
55 case 1: A shell boolean arg, which doesn't need to be re-worded in python.
56 case 2: A shell boolean arg, which needs to be re-worded in python.
57 Example below.
58 For Case 1, for a given input arg name 'argA', we create three python
59 arguments.
60 --argA, --noargA, --no-argA. The arguments --argA and --no-argA will be
61 retained after deprecating --noargA.
62 For Case 2, for a given input arg name 'arg_A' we need to use alternate
63 argument name 'arg-A'. we create four python arguments in this case.
64 --arg_A, --noarg_A, --arg-A, --no-arg-A. The first two arguments will be
65 deprecated later.
66 TODO(b/232566937): Remove the creation of --noargA in case 1 and --arg_A and
67 --noarg_A in case 2.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000068
Alex Klein1699fab2022-09-08 08:46:06 -060069 Args:
70 parser: The parser to update.
71 name: The input argument name. This will be used as 'dest' variable name.
72 default_val: The default value to assign.
73 help_str: The help string for the input argument.
74 deprecation_note: A deprecation note to use.
75 alternate_name: Alternate argument to be used after deprecation.
76 additional_neg_options: Additional negative alias options to use.
77 """
78 arg = f"--{name}"
79 shell_narg = f"--no{name}"
80 py_narg = f"--no-{name}"
81 alt_arg = f"--{alternate_name}" if alternate_name else None
82 alt_py_narg = f"--no-{alternate_name}" if alternate_name else None
83 default_val_str = f"{help_str} (Default: %(default)s)."
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000084
Alex Klein1699fab2022-09-08 08:46:06 -060085 if alternate_name:
86 _alternate_narg_list = [alt_py_narg]
87 if additional_neg_options:
88 _alternate_narg_list.extend(additional_neg_options)
89
90 parser.add_argument(
91 alt_arg,
92 action="store_true",
93 default=default_val,
94 dest=name,
95 help=default_val_str,
96 )
97 parser.add_argument(
98 *_alternate_narg_list,
99 action="store_false",
100 dest=name,
101 help="Don't " + help_str.lower(),
102 )
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000103
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000104 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600105 arg,
106 action="store_true",
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000107 default=default_val,
108 dest=name,
Alex Klein1699fab2022-09-08 08:46:06 -0600109 deprecated=deprecation_note % alt_arg if alternate_name else None,
110 help=default_val_str if not alternate_name else argparse.SUPPRESS,
111 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000112 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600113 shell_narg,
114 action="store_false",
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000115 dest=name,
Alex Klein1699fab2022-09-08 08:46:06 -0600116 deprecated=deprecation_note
117 % (alt_py_narg if alternate_name else py_narg),
118 help=argparse.SUPPRESS,
119 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000120
Alex Klein1699fab2022-09-08 08:46:06 -0600121 if not alternate_name:
122 _py_narg_list = [py_narg]
123 if additional_neg_options:
124 _py_narg_list.extend(additional_neg_options)
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000125
Alex Klein1699fab2022-09-08 08:46:06 -0600126 parser.add_argument(
127 *_py_narg_list,
128 action="store_false",
129 dest=name,
130 help="Don't " + help_str.lower(),
131 )
132
133
134def build_shell_string_style_args(
135 parser: commandline.ArgumentParser,
136 name: str,
137 default_val: Optional[str],
138 help_str: str,
139 deprecation_note: str,
140 alternate_name: str,
141) -> None:
142 """Build the shell string input argument equivalent.
143
144 Args:
145 parser: The parser to update.
146 name: The input argument name. This will be used as 'dest' variable name.
147 default_val: The default value to assign.
148 help_str: The help string for the input argument.
149 deprecation_note: A deprecation note to use.
150 alternate_name: Alternate argument to be used after deprecation.
151 """
152 default_val_str = (
153 f"{help_str} (Default: %(default)s)." if default_val else help_str
154 )
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000155
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000156 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600157 f"--{alternate_name}",
158 dest=f"{name}",
159 default=default_val,
160 help=default_val_str,
161 )
162 parser.add_argument(
163 f"--{name}",
164 deprecated=deprecation_note % f"--{alternate_name}",
165 help=argparse.SUPPRESS,
166 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000167
168
169def get_parser() -> commandline.ArgumentParser:
Alex Klein1699fab2022-09-08 08:46:06 -0600170 """Creates the cmdline argparser, populates the options and description.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000171
Alex Klein1699fab2022-09-08 08:46:06 -0600172 Returns:
173 Argument parser.
174 """
175 deprecation_note = "Argument will be removed January, 2023. Use %s instead."
176 parser = commandline.ArgumentParser(description=__doc__)
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000177
Alex Klein1699fab2022-09-08 08:46:06 -0600178 parser.add_argument(
179 "-b",
180 "--board",
181 "--build-target",
182 dest="board",
183 default=cros_build_lib.GetDefaultBoard(),
184 help="The board to build images for.",
185 )
186 build_shell_string_style_args(
187 parser,
188 "adjust_part",
189 None,
190 "Adjustments to apply to partition table (LABEL:[+-=]SIZE) "
191 "e.g. ROOT-A:+1G.",
192 deprecation_note,
193 "adjust-partition",
194 )
195 build_shell_string_style_args(
196 parser,
197 "output_root",
198 Path(constants.DEFAULT_BUILD_ROOT) / "images",
199 "Directory in which to place image result directories "
200 "(named by version).",
201 deprecation_note,
202 "output-root",
203 )
204 build_shell_string_style_args(
205 parser,
206 "builder_path",
207 None,
208 "The build name to be installed on DUT during hwtest.",
209 deprecation_note,
210 "builder-path",
211 )
212 build_shell_string_style_args(
213 parser,
214 "disk_layout",
215 "default",
216 "The disk layout type to use for this image.",
217 deprecation_note,
218 "disk-layout",
219 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000220
Alex Klein1699fab2022-09-08 08:46:06 -0600221 # Kernel related options.
222 group = parser.add_argument_group("Kernel Options")
223 build_shell_string_style_args(
224 group,
225 "enable_serial",
226 None,
227 "Enable serial port for printks. Example values: ttyS0.",
228 deprecation_note,
229 "enable-serial",
230 )
231 group.add_argument(
232 "--kernel-loglevel",
233 type=int,
234 default=7,
235 help="The loglevel to add to the kernel command line. "
236 "(Default: %(default)s).",
237 )
238 group.add_argument(
239 "--loglevel",
240 dest="kernel_loglevel",
241 type=int,
242 deprecated=deprecation_note % "kernel-loglevel",
243 help=argparse.SUPPRESS,
244 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000245
Alex Klein1699fab2022-09-08 08:46:06 -0600246 # Bootloader related options.
247 group = parser.add_argument_group("Bootloader Options")
248 build_shell_string_style_args(
249 group,
250 "boot_args",
251 "noinitrd",
252 "Additional boot arguments to pass to the commandline.",
253 deprecation_note,
254 "boot-args",
255 )
256 build_shell_bool_style_args(
257 group,
258 "enable_bootcache",
259 False,
260 "Make all bootloaders to use boot cache.",
261 deprecation_note,
262 "enable-bootcache",
263 )
264 build_shell_bool_style_args(
265 group,
266 "enable_rootfs_verification",
267 True,
268 "Make all bootloaders to use kernel based root-fs integrity checking.",
269 deprecation_note,
270 "enable-rootfs-verification",
271 ["-r"],
272 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000273
Alex Klein1699fab2022-09-08 08:46:06 -0600274 # Advanced options.
275 group = parser.add_argument_group("Advanced Options")
276 group.add_argument(
277 "--build-attempt",
278 type=int,
279 default=1,
280 help="The build attempt for this image build. (Default: %(default)s).",
281 )
282 group.add_argument(
283 "--build_attempt",
284 type=int,
285 deprecated=deprecation_note % "build-attempt",
286 help=argparse.SUPPRESS,
287 )
288 build_shell_string_style_args(
289 group,
290 "build_root",
291 Path(constants.DEFAULT_BUILD_ROOT) / "images",
292 "Directory in which to compose the image, before copying it to "
293 "output_root.",
294 deprecation_note,
295 "build-root",
296 )
297 group.add_argument(
298 "-j",
299 "--jobs",
300 dest="jobs",
301 type=int,
302 default=os.cpu_count(),
303 help="Number of packages to build in parallel at maximum. "
304 "(Default: %(default)s).",
305 )
306 build_shell_bool_style_args(
307 group,
308 "replace",
309 False,
310 "Overwrite existing output, if any.",
311 deprecation_note,
312 )
313 group.add_argument(
314 "--symlink",
315 default="latest",
316 help="Symlink name to use for this image. (Default: %(default)s).",
317 )
318 group.add_argument(
319 "--version",
320 default=None,
321 help="Overrides version number in name to this version.",
322 )
323 build_shell_string_style_args(
324 group,
325 "output_suffix",
326 None,
327 "Add custom suffix to output directory.",
328 deprecation_note,
329 "output-suffix",
330 )
331 group.add_argument(
332 "--eclean",
333 action="store_true",
334 default=True,
335 dest="eclean",
336 deprecated=(
337 "eclean is being removed from build_images. Argument will be "
338 "removed January, 2023."
339 ),
340 help=argparse.SUPPRESS,
341 )
342 group.add_argument(
343 "--noeclean",
344 action="store_false",
345 dest="eclean",
346 deprecated=(
347 "eclean is being removed from build_images. Argument will be "
348 "removed January, 2023."
349 ),
350 help=argparse.SUPPRESS,
351 )
352 group.add_argument(
353 "--no-eclean",
354 action="store_false",
355 dest="eclean",
356 deprecated=(
357 "eclean is being removed from build_images. Argument will be "
358 "removed January, 2023."
359 ),
360 help=argparse.SUPPRESS,
361 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000362
Alex Klein1699fab2022-09-08 08:46:06 -0600363 parser.add_argument(
364 "images",
365 nargs="*",
366 default=["dev"],
367 help="list of images to build. (Default: %(default)s).",
368 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000369
Alex Klein1699fab2022-09-08 08:46:06 -0600370 return parser
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000371
372
373def parse_args(
Alex Klein1699fab2022-09-08 08:46:06 -0600374 argv: List[str],
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000375) -> Tuple[commandline.ArgumentParser, commandline.ArgumentNamespace]:
Alex Klein1699fab2022-09-08 08:46:06 -0600376 """Parse and validate CLI arguments.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000377
Alex Klein1699fab2022-09-08 08:46:06 -0600378 Args:
379 argv: Arguments passed via CLI.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 Returns:
382 Tuple having the below two,
383 Argument Parser
384 Validated argument namespace.
385 """
386 parser = get_parser()
387 opts = parser.parse_args(argv)
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000388
Alex Klein1699fab2022-09-08 08:46:06 -0600389 opts.build_run_config = image.BuildConfig(
390 adjust_partition=opts.adjust_part,
391 output_root=opts.output_root,
392 builder_path=opts.builder_path,
393 disk_layout=opts.disk_layout,
394 enable_serial=opts.enable_serial,
395 kernel_loglevel=opts.kernel_loglevel,
396 boot_args=opts.boot_args,
397 enable_bootcache=opts.enable_bootcache,
398 enable_rootfs_verification=opts.enable_rootfs_verification,
399 build_attempt=opts.build_attempt,
400 build_root=opts.build_root,
401 jobs=opts.jobs,
402 replace=opts.replace,
403 symlink=opts.symlink,
404 version=opts.version,
405 output_dir_suffix=opts.output_suffix,
406 )
407 opts.Freeze()
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000408
Alex Klein1699fab2022-09-08 08:46:06 -0600409 return parser, opts
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000410
411
Alex Klein1699fab2022-09-08 08:46:06 -0600412@timer.timed("Elapsed time (build_image)")
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000413def main(argv: Optional[List[str]] = None) -> Optional[int]:
Alex Klein1699fab2022-09-08 08:46:06 -0600414 commandline.RunInsideChroot()
415 parser, opts = parse_args(argv)
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 # If the opts.board is not set, then it means user hasn't specified a default
418 # board in 'src/scripts/.default_board' and didn't specify it as input
419 # argument.
420 if not opts.board:
421 parser.error("--board is required")
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000422
Alex Klein1699fab2022-09-08 08:46:06 -0600423 invalid_image = [
424 x for x in opts.images if x not in constants.IMAGE_TYPE_TO_NAME
425 ]
426 if invalid_image:
427 parser.error(f"Invalid image type argument(s) {invalid_image}")
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000428
Alex Klein1699fab2022-09-08 08:46:06 -0600429 result = image.Build(opts.board, opts.images, opts.build_run_config)
430 if result.run_error:
431 cros_build_lib.Die(
432 f"Error running build_image. Exit Code : {result.return_code}"
433 )