blob: a7afd67c24d1ea62c273a748dd5f65982c0fdd0c [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
Cindy Lin0abfec02022-09-01 18:25:30 +000034import sys
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000035from typing import List, Optional, Tuple
Ram Chandrasekar913c0422022-05-09 23:41:22 +000036
37from chromite.lib import commandline
38from chromite.lib import constants
39from chromite.lib import cros_build_lib
Cindy Lin0abfec02022-09-01 18:25:30 +000040from chromite.lib import namespaces
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000041from chromite.service import image
Ram Chandrasekar158b32a2022-07-15 17:06:13 +000042from chromite.utils import timer
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000043
44
Ram Chandrasekarf005fa72022-06-16 21:45:14 +000045def build_shell_bool_style_args(
46 parser: commandline.ArgumentParser,
47 name: str,
48 default_val: bool,
49 help_str: str,
50 deprecation_note: str,
51 alternate_name: Optional[str] = None,
Alex Klein1699fab2022-09-08 08:46:06 -060052 additional_neg_options: Optional[List[str]] = None,
53) -> None:
54 """Build the shell boolean input argument equivalent.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000055
Alex Klein1699fab2022-09-08 08:46:06 -060056 There are two cases which we will need to handle,
57 case 1: A shell boolean arg, which doesn't need to be re-worded in python.
58 case 2: A shell boolean arg, which needs to be re-worded in python.
59 Example below.
60 For Case 1, for a given input arg name 'argA', we create three python
61 arguments.
62 --argA, --noargA, --no-argA. The arguments --argA and --no-argA will be
63 retained after deprecating --noargA.
64 For Case 2, for a given input arg name 'arg_A' we need to use alternate
65 argument name 'arg-A'. we create four python arguments in this case.
66 --arg_A, --noarg_A, --arg-A, --no-arg-A. The first two arguments will be
67 deprecated later.
68 TODO(b/232566937): Remove the creation of --noargA in case 1 and --arg_A and
69 --noarg_A in case 2.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000070
Alex Klein1699fab2022-09-08 08:46:06 -060071 Args:
72 parser: The parser to update.
73 name: The input argument name. This will be used as 'dest' variable name.
74 default_val: The default value to assign.
75 help_str: The help string for the input argument.
76 deprecation_note: A deprecation note to use.
77 alternate_name: Alternate argument to be used after deprecation.
78 additional_neg_options: Additional negative alias options to use.
79 """
80 arg = f"--{name}"
81 shell_narg = f"--no{name}"
82 py_narg = f"--no-{name}"
83 alt_arg = f"--{alternate_name}" if alternate_name else None
84 alt_py_narg = f"--no-{alternate_name}" if alternate_name else None
85 default_val_str = f"{help_str} (Default: %(default)s)."
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +000086
Alex Klein1699fab2022-09-08 08:46:06 -060087 if alternate_name:
88 _alternate_narg_list = [alt_py_narg]
89 if additional_neg_options:
90 _alternate_narg_list.extend(additional_neg_options)
91
92 parser.add_argument(
93 alt_arg,
94 action="store_true",
95 default=default_val,
96 dest=name,
97 help=default_val_str,
98 )
99 parser.add_argument(
100 *_alternate_narg_list,
101 action="store_false",
102 dest=name,
103 help="Don't " + help_str.lower(),
104 )
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000105
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000106 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600107 arg,
108 action="store_true",
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000109 default=default_val,
110 dest=name,
Alex Klein1699fab2022-09-08 08:46:06 -0600111 deprecated=deprecation_note % alt_arg if alternate_name else None,
112 help=default_val_str if not alternate_name else argparse.SUPPRESS,
113 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000114 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600115 shell_narg,
116 action="store_false",
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000117 dest=name,
Alex Klein1699fab2022-09-08 08:46:06 -0600118 deprecated=deprecation_note
119 % (alt_py_narg if alternate_name else py_narg),
120 help=argparse.SUPPRESS,
121 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000122
Alex Klein1699fab2022-09-08 08:46:06 -0600123 if not alternate_name:
124 _py_narg_list = [py_narg]
125 if additional_neg_options:
126 _py_narg_list.extend(additional_neg_options)
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000127
Alex Klein1699fab2022-09-08 08:46:06 -0600128 parser.add_argument(
129 *_py_narg_list,
130 action="store_false",
131 dest=name,
132 help="Don't " + help_str.lower(),
133 )
134
135
136def build_shell_string_style_args(
137 parser: commandline.ArgumentParser,
138 name: str,
139 default_val: Optional[str],
140 help_str: str,
141 deprecation_note: str,
142 alternate_name: str,
143) -> None:
144 """Build the shell string input argument equivalent.
145
146 Args:
147 parser: The parser to update.
148 name: The input argument name. This will be used as 'dest' variable name.
149 default_val: The default value to assign.
150 help_str: The help string for the input argument.
151 deprecation_note: A deprecation note to use.
152 alternate_name: Alternate argument to be used after deprecation.
153 """
154 default_val_str = (
155 f"{help_str} (Default: %(default)s)." if default_val else help_str
156 )
Ram Chandrasekarf005fa72022-06-16 21:45:14 +0000157
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000158 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600159 f"--{alternate_name}",
160 dest=f"{name}",
161 default=default_val,
162 help=default_val_str,
163 )
164 parser.add_argument(
165 f"--{name}",
166 deprecated=deprecation_note % f"--{alternate_name}",
167 help=argparse.SUPPRESS,
168 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000169
170
171def get_parser() -> commandline.ArgumentParser:
Alex Klein1699fab2022-09-08 08:46:06 -0600172 """Creates the cmdline argparser, populates the options and description.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000173
Alex Klein1699fab2022-09-08 08:46:06 -0600174 Returns:
175 Argument parser.
176 """
177 deprecation_note = "Argument will be removed January, 2023. Use %s instead."
178 parser = commandline.ArgumentParser(description=__doc__)
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000179
Alex Klein1699fab2022-09-08 08:46:06 -0600180 parser.add_argument(
181 "-b",
182 "--board",
183 "--build-target",
184 dest="board",
185 default=cros_build_lib.GetDefaultBoard(),
186 help="The board to build images for.",
187 )
188 build_shell_string_style_args(
189 parser,
190 "adjust_part",
191 None,
192 "Adjustments to apply to partition table (LABEL:[+-=]SIZE) "
193 "e.g. ROOT-A:+1G.",
194 deprecation_note,
195 "adjust-partition",
196 )
197 build_shell_string_style_args(
198 parser,
199 "output_root",
200 Path(constants.DEFAULT_BUILD_ROOT) / "images",
201 "Directory in which to place image result directories "
202 "(named by version).",
203 deprecation_note,
204 "output-root",
205 )
206 build_shell_string_style_args(
207 parser,
208 "builder_path",
209 None,
210 "The build name to be installed on DUT during hwtest.",
211 deprecation_note,
212 "builder-path",
213 )
214 build_shell_string_style_args(
215 parser,
216 "disk_layout",
217 "default",
218 "The disk layout type to use for this image.",
219 deprecation_note,
220 "disk-layout",
221 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000222
Alex Klein1699fab2022-09-08 08:46:06 -0600223 # Kernel related options.
224 group = parser.add_argument_group("Kernel Options")
225 build_shell_string_style_args(
226 group,
227 "enable_serial",
228 None,
229 "Enable serial port for printks. Example values: ttyS0.",
230 deprecation_note,
231 "enable-serial",
232 )
233 group.add_argument(
234 "--kernel-loglevel",
235 type=int,
236 default=7,
237 help="The loglevel to add to the kernel command line. "
238 "(Default: %(default)s).",
239 )
240 group.add_argument(
241 "--loglevel",
242 dest="kernel_loglevel",
243 type=int,
244 deprecated=deprecation_note % "kernel-loglevel",
245 help=argparse.SUPPRESS,
246 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000247
Alex Klein1699fab2022-09-08 08:46:06 -0600248 # Bootloader related options.
249 group = parser.add_argument_group("Bootloader Options")
250 build_shell_string_style_args(
251 group,
252 "boot_args",
253 "noinitrd",
254 "Additional boot arguments to pass to the commandline.",
255 deprecation_note,
256 "boot-args",
257 )
258 build_shell_bool_style_args(
259 group,
260 "enable_bootcache",
261 False,
262 "Make all bootloaders to use boot cache.",
263 deprecation_note,
264 "enable-bootcache",
265 )
266 build_shell_bool_style_args(
267 group,
268 "enable_rootfs_verification",
269 True,
270 "Make all bootloaders to use kernel based root-fs integrity checking.",
271 deprecation_note,
272 "enable-rootfs-verification",
273 ["-r"],
274 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000275
Alex Klein1699fab2022-09-08 08:46:06 -0600276 # Advanced options.
277 group = parser.add_argument_group("Advanced Options")
278 group.add_argument(
279 "--build-attempt",
280 type=int,
281 default=1,
282 help="The build attempt for this image build. (Default: %(default)s).",
283 )
284 group.add_argument(
285 "--build_attempt",
286 type=int,
287 deprecated=deprecation_note % "build-attempt",
288 help=argparse.SUPPRESS,
289 )
290 build_shell_string_style_args(
291 group,
292 "build_root",
293 Path(constants.DEFAULT_BUILD_ROOT) / "images",
294 "Directory in which to compose the image, before copying it to "
295 "output_root.",
296 deprecation_note,
297 "build-root",
298 )
299 group.add_argument(
300 "-j",
301 "--jobs",
302 dest="jobs",
303 type=int,
304 default=os.cpu_count(),
305 help="Number of packages to build in parallel at maximum. "
306 "(Default: %(default)s).",
307 )
308 build_shell_bool_style_args(
309 group,
310 "replace",
311 False,
312 "Overwrite existing output, if any.",
313 deprecation_note,
314 )
315 group.add_argument(
316 "--symlink",
317 default="latest",
318 help="Symlink name to use for this image. (Default: %(default)s).",
319 )
320 group.add_argument(
321 "--version",
322 default=None,
323 help="Overrides version number in name to this version.",
324 )
325 build_shell_string_style_args(
326 group,
327 "output_suffix",
328 None,
329 "Add custom suffix to output directory.",
330 deprecation_note,
331 "output-suffix",
332 )
333 group.add_argument(
334 "--eclean",
335 action="store_true",
336 default=True,
337 dest="eclean",
338 deprecated=(
339 "eclean is being removed from build_images. Argument will be "
340 "removed January, 2023."
341 ),
342 help=argparse.SUPPRESS,
343 )
344 group.add_argument(
345 "--noeclean",
346 action="store_false",
347 dest="eclean",
348 deprecated=(
349 "eclean is being removed from build_images. Argument will be "
350 "removed January, 2023."
351 ),
352 help=argparse.SUPPRESS,
353 )
354 group.add_argument(
355 "--no-eclean",
356 action="store_false",
357 dest="eclean",
358 deprecated=(
359 "eclean is being removed from build_images. Argument will be "
360 "removed January, 2023."
361 ),
362 help=argparse.SUPPRESS,
363 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000364
Alex Klein1699fab2022-09-08 08:46:06 -0600365 parser.add_argument(
366 "images",
367 nargs="*",
368 default=["dev"],
369 help="list of images to build. (Default: %(default)s).",
370 )
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000371
Alex Klein1699fab2022-09-08 08:46:06 -0600372 return parser
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000373
374
375def parse_args(
Alex Klein1699fab2022-09-08 08:46:06 -0600376 argv: List[str],
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000377) -> Tuple[commandline.ArgumentParser, commandline.ArgumentNamespace]:
Alex Klein1699fab2022-09-08 08:46:06 -0600378 """Parse and validate CLI arguments.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000379
Alex Klein1699fab2022-09-08 08:46:06 -0600380 Args:
381 argv: Arguments passed via CLI.
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000382
Alex Klein1699fab2022-09-08 08:46:06 -0600383 Returns:
384 Tuple having the below two,
385 Argument Parser
386 Validated argument namespace.
387 """
388 parser = get_parser()
389 opts = parser.parse_args(argv)
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000390
Alex Klein1699fab2022-09-08 08:46:06 -0600391 opts.build_run_config = image.BuildConfig(
392 adjust_partition=opts.adjust_part,
393 output_root=opts.output_root,
394 builder_path=opts.builder_path,
395 disk_layout=opts.disk_layout,
396 enable_serial=opts.enable_serial,
397 kernel_loglevel=opts.kernel_loglevel,
398 boot_args=opts.boot_args,
399 enable_bootcache=opts.enable_bootcache,
400 enable_rootfs_verification=opts.enable_rootfs_verification,
401 build_attempt=opts.build_attempt,
402 build_root=opts.build_root,
403 jobs=opts.jobs,
404 replace=opts.replace,
405 symlink=opts.symlink,
406 version=opts.version,
407 output_dir_suffix=opts.output_suffix,
408 )
409 opts.Freeze()
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000410
Alex Klein1699fab2022-09-08 08:46:06 -0600411 return parser, opts
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000412
413
Alex Klein1699fab2022-09-08 08:46:06 -0600414@timer.timed("Elapsed time (build_image)")
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000415def main(argv: Optional[List[str]] = None) -> Optional[int]:
Alex Klein1699fab2022-09-08 08:46:06 -0600416 commandline.RunInsideChroot()
Cindy Lin0abfec02022-09-01 18:25:30 +0000417
418 # Make sure we run with network disabled to prevent leakage.
419 namespaces.ReExecuteWithNamespace(sys.argv, preserve_env=True)
420
Alex Klein1699fab2022-09-08 08:46:06 -0600421 parser, opts = parse_args(argv)
Ram Chandrasekar913c0422022-05-09 23:41:22 +0000422
Alex Klein1699fab2022-09-08 08:46:06 -0600423 # If the opts.board is not set, then it means user hasn't specified a default
424 # board in 'src/scripts/.default_board' and didn't specify it as input
425 # argument.
426 if not opts.board:
427 parser.error("--board is required")
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000428
Alex Klein1699fab2022-09-08 08:46:06 -0600429 invalid_image = [
430 x for x in opts.images if x not in constants.IMAGE_TYPE_TO_NAME
431 ]
432 if invalid_image:
433 parser.error(f"Invalid image type argument(s) {invalid_image}")
Ram Chandrasekar355c8cc2022-05-24 23:10:30 +0000434
Alex Klein1699fab2022-09-08 08:46:06 -0600435 result = image.Build(opts.board, opts.images, opts.build_run_config)
436 if result.run_error:
437 cros_build_lib.Die(
438 f"Error running build_image. Exit Code : {result.return_code}"
439 )