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