blob: 51dc6fe7ce730aed53c70cfffab262563473a4a2 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassfc3fe1c2013-04-03 11:07:16 +00002# Copyright (c) 2013 The Chromium OS Authors.
3#
Simon Glassfc3fe1c2013-04-03 11:07:16 +00004
Simon Glass9ef05b92023-07-19 17:48:30 -06005"""Control module for buildman
6
7This holds the main control logic for buildman, when not running tests.
8"""
9
Simon Glassfc3fe1c2013-04-03 11:07:16 +000010import multiprocessing
11import os
Simon Glass883a3212014-09-05 19:00:18 -060012import shutil
Simon Glassfc3fe1c2013-04-03 11:07:16 +000013import sys
14
Simon Glassc52bd222022-07-11 19:04:03 -060015from buildman import boards
Simon Glass0ede00f2020-04-17 18:09:02 -060016from buildman import bsettings
Simon Glass2b4806e2022-01-22 05:07:33 -070017from buildman import cfgutil
Simon Glass0ede00f2020-04-17 18:09:02 -060018from buildman import toolchain
19from buildman.builder import Builder
Simon Glassbf776672020-04-17 18:09:04 -060020from patman import gitutil
21from patman import patchstream
Simon Glass4583c002023-02-23 18:18:04 -070022from u_boot_pylib import command
23from u_boot_pylib import terminal
Simon Glass4583c002023-02-23 18:18:04 -070024from u_boot_pylib.terminal import tprint
Simon Glassfc3fe1c2013-04-03 11:07:16 +000025
Simon Glassb8be2bd2023-07-19 17:48:31 -060026TEST_BUILDER = None
27
Simon Glass9ef05b92023-07-19 17:48:30 -060028def get_plural(count):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000029 """Returns a plural 's' if count is not 1"""
30 return 's' if count != 1 else ''
31
Simon Glass1d3a5a52023-07-19 17:48:45 -060032def get_action_summary(is_summary, commits, selected, step, threads, jobs):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000033 """Return a string summarising the intended action.
34
Simon Glass1d3a5a52023-07-19 17:48:45 -060035 Args:
36 is_summary (bool): True if this is a summary (otherwise it is building)
37 commits (list): List of commits being built
38 selected (list of Board): List of Board objects that are marked
39 step (int): Step increment through commits
40 threads (int): Number of processor threads being used
41 jobs (int): Number of jobs to build at once
42
Simon Glassfc3fe1c2013-04-03 11:07:16 +000043 Returns:
44 Summary string.
45 """
Simon Glassfea58582014-08-09 15:32:59 -060046 if commits:
47 count = len(commits)
Simon Glass1d3a5a52023-07-19 17:48:45 -060048 count = (count + step - 1) // step
Simon Glassb8be2bd2023-07-19 17:48:31 -060049 commit_str = f'{count} commit{get_plural(count)}'
Simon Glassfea58582014-08-09 15:32:59 -060050 else:
51 commit_str = 'current source'
Simon Glassb8be2bd2023-07-19 17:48:31 -060052 msg = (f"{'Summary of' if is_summary else 'Building'} "
53 f'{commit_str} for {len(selected)} boards')
Simon Glass1d3a5a52023-07-19 17:48:45 -060054 msg += (f' ({threads} thread{get_plural(threads)}, '
55 f'{jobs} job{get_plural(jobs)} per thread)')
Simon Glassb8be2bd2023-07-19 17:48:31 -060056 return msg
Simon Glassfc3fe1c2013-04-03 11:07:16 +000057
Simon Glassb8be2bd2023-07-19 17:48:31 -060058# pylint: disable=R0913
Simon Glass1b820ee2023-07-19 17:48:46 -060059def show_actions(series, why_selected, boards_selected, output_dir,
60 board_warnings, step, threads, jobs, verbose):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000061 """Display a list of actions that we would take, if not a dry run.
62
63 Args:
64 series: Series object
65 why_selected: Dictionary where each key is a buildman argument
Simon Glass8d7523c2017-01-23 05:38:56 -070066 provided by the user, and the value is the list of boards
67 brought in by that argument. For example, 'arm' might bring
68 in 400 boards, so in this case the key would be 'arm' and
Simon Glassfc3fe1c2013-04-03 11:07:16 +000069 the value would be a list of board names.
70 boards_selected: Dict of selected boards, key is target name,
71 value is Board object
Simon Glassd233dfb2023-07-19 17:48:36 -060072 output_dir (str): Output directory for builder
Simon Glass06890362018-06-11 23:26:46 -060073 board_warnings: List of warnings obtained from board selected
Simon Glass1b820ee2023-07-19 17:48:46 -060074 step (int): Step increment through commits
75 threads (int): Number of processor threads being used
76 jobs (int): Number of jobs to build at once
77 verbose (bool): True to indicate why each board was selected
Simon Glassfc3fe1c2013-04-03 11:07:16 +000078 """
79 col = terminal.Color()
Simon Glassc05aa032019-10-31 07:42:53 -060080 print('Dry run, so not doing much. But I would do this:')
81 print()
Simon Glassfea58582014-08-09 15:32:59 -060082 if series:
83 commits = series.commits
84 else:
85 commits = None
Simon Glass1b820ee2023-07-19 17:48:46 -060086 print(get_action_summary(False, commits, boards_selected, step, threads,
87 jobs))
Simon Glassd233dfb2023-07-19 17:48:36 -060088 print(f'Build directory: {output_dir}')
Simon Glassfea58582014-08-09 15:32:59 -060089 if commits:
Simon Glass1b820ee2023-07-19 17:48:46 -060090 for upto in range(0, len(series.commits), step):
Simon Glassfea58582014-08-09 15:32:59 -060091 commit = series.commits[upto]
Simon Glass252ac582022-01-29 14:14:17 -070092 print(' ', col.build(col.YELLOW, commit.hash[:8], bright=False), end=' ')
Simon Glassc05aa032019-10-31 07:42:53 -060093 print(commit.subject)
94 print()
Simon Glassfc3fe1c2013-04-03 11:07:16 +000095 for arg in why_selected:
96 if arg != 'all':
Simon Glassb8be2bd2023-07-19 17:48:31 -060097 print(arg, f': {len(why_selected[arg])} boards')
Simon Glass1b820ee2023-07-19 17:48:46 -060098 if verbose:
Simon Glassb8be2bd2023-07-19 17:48:31 -060099 print(f" {' '.join(why_selected[arg])}")
100 print('Total boards to build for each '
101 f"commit: {len(why_selected['all'])}\n")
Simon Glass06890362018-06-11 23:26:46 -0600102 if board_warnings:
103 for warning in board_warnings:
Simon Glass252ac582022-01-29 14:14:17 -0700104 print(col.build(col.YELLOW, warning))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000105
Simon Glass9ef05b92023-07-19 17:48:30 -0600106def show_toolchain_prefix(brds, toolchains):
Simon Glass57cb9d52019-12-05 15:59:14 -0700107 """Show information about a the tool chain used by one or more boards
108
Simon Glass4e9162d2020-03-18 09:42:47 -0600109 The function checks that all boards use the same toolchain, then prints
110 the correct value for CROSS_COMPILE.
Simon Glass57cb9d52019-12-05 15:59:14 -0700111
112 Args:
113 boards: Boards object containing selected boards
114 toolchains: Toolchains object containing available toolchains
Simon Glass57cb9d52019-12-05 15:59:14 -0700115
116 Return:
117 None on success, string error message otherwise
118 """
Simon Glass6014db62022-07-11 19:04:02 -0600119 board_selected = brds.get_selected_dict()
Simon Glass57cb9d52019-12-05 15:59:14 -0700120 tc_set = set()
Simon Glasscc2c0d12022-07-11 19:04:00 -0600121 for brd in board_selected.values():
Simon Glass57cb9d52019-12-05 15:59:14 -0700122 tc_set.add(toolchains.Select(brd.arch))
123 if len(tc_set) != 1:
124 return 'Supplied boards must share one toolchain'
Simon Glassb8be2bd2023-07-19 17:48:31 -0600125 tchain = tc_set.pop()
126 print(tchain.GetEnvArgs(toolchain.VAR_CROSS_COMPILE))
Simon Glass57cb9d52019-12-05 15:59:14 -0700127 return None
128
Tom Rinid7713ad2022-11-09 19:14:53 -0700129def get_allow_missing(opt_allow, opt_no_allow, num_selected, has_branch):
Simon Glassb8be2bd2023-07-19 17:48:31 -0600130 """Figure out whether to allow external blobs
131
132 Uses the allow-missing setting and the provided arguments to decide whether
133 missing external blobs should be allowed
134
135 Args:
136 opt_allow (bool): True if --allow-missing flag is set
137 opt_no_allow (bool): True if --no-allow-missing flag is set
138 num_selected (int): Number of selected board
139 has_branch (bool): True if a git branch (to build) has been provided
140
141 Returns:
142 bool: True to allow missing external blobs, False to produce an error if
143 external blobs are used
144 """
Tom Rinid7713ad2022-11-09 19:14:53 -0700145 allow_missing = False
146 am_setting = bsettings.GetGlobalItemValue('allow-missing')
147 if am_setting:
148 if am_setting == 'always':
149 allow_missing = True
150 if 'multiple' in am_setting and num_selected > 1:
151 allow_missing = True
152 if 'branch' in am_setting and has_branch:
153 allow_missing = True
154
155 if opt_allow:
156 allow_missing = True
157 if opt_no_allow:
158 allow_missing = False
159 return allow_missing
160
Simon Glassd230c012023-07-19 17:48:33 -0600161
Simon Glassaeb23812023-07-19 17:48:48 -0600162def count_commits(branch, count, col, git_dir):
163 """Could the number of commits in the branch/ranch being built
164
165 Args:
166 branch (str): Name of branch to build, or None if none
167 count (int): Number of commits to build, or -1 for all
168 col (Terminal.Color): Color object to use
169 git_dir (str): Git directory to use, e.g. './.git'
170
171 Returns:
172 tuple:
173 Number of commits being built
174 True if the 'branch' string contains a range rather than a simple
175 name
176 """
177 has_range = branch and '..' in branch
178 if count == -1:
179 if not branch:
180 count = 1
181 else:
182 if has_range:
183 count, msg = gitutil.count_commits_in_range(git_dir, branch)
184 else:
185 count, msg = gitutil.count_commits_in_branch(git_dir, branch)
186 if count is None:
187 sys.exit(col.build(col.RED, msg))
188 elif count == 0:
189 sys.exit(col.build(col.RED,
190 f"Range '{branch}' has no commits"))
191 if msg:
192 print(col.build(col.YELLOW, msg))
193 count += 1 # Build upstream commit also
194
195 if not count:
196 msg = (f"No commits found to process in branch '{branch}': "
197 "set branch's upstream or use -c flag")
198 sys.exit(col.build(col.RED, msg))
199 return count, has_range
200
201
Simon Glass9df59e42023-07-19 17:48:40 -0600202def determine_series(selected, col, git_dir, count, branch, work_in_output):
Simon Glassd230c012023-07-19 17:48:33 -0600203 """Determine the series which is to be built, if any
204
Simon Glass6378bad2023-07-19 17:48:50 -0600205 If there is a series, the commits in that series are numbered by setting
206 their sequence value (starting from 0). This is used by tests.
207
Simon Glassd230c012023-07-19 17:48:33 -0600208 Args:
Simon Glass1d3a5a52023-07-19 17:48:45 -0600209 selected (list of Board): List of Board objects that are marked
Simon Glass9df59e42023-07-19 17:48:40 -0600210 selected
211 col (Terminal.Color): Color object to use
Simon Glassd230c012023-07-19 17:48:33 -0600212 git_dir (str): Git directory to use, e.g. './.git'
Simon Glass9df59e42023-07-19 17:48:40 -0600213 count (int): Number of commits in branch
214 branch (str): Name of branch to build, or None if none
215 work_in_output (bool): True to work in the output directory
Simon Glassd230c012023-07-19 17:48:33 -0600216
217 Returns:
218 Series: Series to build, or None for none
219
220 Read the metadata from the commits. First look at the upstream commit,
221 then the ones in the branch. We would like to do something like
222 upstream/master~..branch but that isn't possible if upstream/master is
223 a merge commit (it will list all the commits that form part of the
224 merge)
225
226 Conflicting tags are not a problem for buildman, since it does not use
227 them. For example, Series-version is not useful for buildman. On the
228 other hand conflicting tags will cause an error. So allow later tags
229 to overwrite earlier ones by setting allow_overwrite=True
230 """
Simon Glass9df59e42023-07-19 17:48:40 -0600231
232 # Work out how many commits to build. We want to build everything on the
233 # branch. We also build the upstream commit as a control so we can see
234 # problems introduced by the first commit on the branch.
Simon Glassaeb23812023-07-19 17:48:48 -0600235 count, has_range = count_commits(branch, count, col, git_dir)
Simon Glass9df59e42023-07-19 17:48:40 -0600236 if work_in_output:
237 if len(selected) != 1:
238 sys.exit(col.build(col.RED,
239 '-w can only be used with a single board'))
240 if count != 1:
241 sys.exit(col.build(col.RED,
242 '-w can only be used with a single commit'))
243
Simon Glassd230c012023-07-19 17:48:33 -0600244 if branch:
245 if count == -1:
246 if has_range:
247 range_expr = branch
248 else:
249 range_expr = gitutil.get_range_in_branch(git_dir, branch)
250 upstream_commit = gitutil.get_upstream(git_dir, branch)
251 series = patchstream.get_metadata_for_list(upstream_commit,
252 git_dir, 1, series=None, allow_overwrite=True)
253
254 series = patchstream.get_metadata_for_list(range_expr,
255 git_dir, None, series, allow_overwrite=True)
256 else:
257 # Honour the count
258 series = patchstream.get_metadata_for_list(branch,
259 git_dir, count, series=None, allow_overwrite=True)
Simon Glass6378bad2023-07-19 17:48:50 -0600260
261 # Number the commits for test purposes
262 for i, commit in enumerate(series.commits):
263 commit.sequence = i
Simon Glassd230c012023-07-19 17:48:33 -0600264 else:
265 series = None
266 return series
267
268
Simon Glassf7a36d52023-07-19 17:48:34 -0600269def do_fetch_arch(toolchains, col, fetch_arch):
270 """Handle the --fetch-arch option
271
272 Args:
273 toolchains (Toolchains): Tool chains to use
274 col (terminal.Color): Color object to build
275 fetch_arch (str): Argument passed to the --fetch-arch option
276
277 Returns:
278 int: Return code for buildman
279 """
280 if fetch_arch == 'list':
281 sorted_list = toolchains.ListArchs()
282 print(col.build(
283 col.BLUE,
284 f"Available architectures: {' '.join(sorted_list)}\n"))
285 return 0
286
287 if fetch_arch == 'all':
288 fetch_arch = ','.join(toolchains.ListArchs())
289 print(col.build(col.CYAN,
290 f'\nDownloading toolchains: {fetch_arch}'))
291 for arch in fetch_arch.split(','):
292 print()
293 ret = toolchains.FetchAndInstall(arch)
294 if ret:
295 return ret
296 return 0
297
298
Simon Glassb8680642023-07-19 17:48:42 -0600299def get_toolchains(toolchains, col, override_toolchain, fetch_arch,
300 list_tool_chains, verbose):
301 """Get toolchains object to use
302
303 Args:
304 toolchains (Toolchains or None): Toolchains to use. If None, then a
305 Toolchains object will be created and scanned
306 col (Terminal.Color): Color object
307 override_toolchain (str or None): Override value for toolchain, or None
308 fetch_arch (bool): True to fetch the toolchain for the architectures
309 list_tool_chains (bool): True to list all tool chains
310 verbose (bool): True for verbose output when listing toolchains
311
312 Returns:
313 Either:
314 int: Operation completed and buildman should exit with exit code
315 Toolchains: Toolchains object to use
316 """
317 no_toolchains = toolchains is None
318 if no_toolchains:
319 toolchains = toolchain.Toolchains(override_toolchain)
320
321 if fetch_arch:
322 return do_fetch_arch(toolchains, col, fetch_arch)
323
324 if no_toolchains:
325 toolchains.GetSettings()
326 toolchains.Scan(list_tool_chains and verbose)
327 if list_tool_chains:
328 toolchains.List()
329 print()
330 return 0
331 return toolchains
332
333
Simon Glass180c7182023-07-19 17:48:41 -0600334def get_boards_obj(output_dir, regen_board_list, maintainer_check, threads,
335 verbose):
336 """Object the Boards object to use
337
338 Creates the output directory and ensures there is a boards.cfg file, then
339 read it in.
340
341 Args:
342 output_dir (str): Output directory to use
343 regen_board_list (bool): True to just regenerate the board list
344 maintainer_check (bool): True to just run a maintainer check
345 threads (int or None): Number of threads to use to create boards file
346 verbose (bool): False to suppress output from boards-file generation
347
348 Returns:
349 Either:
350 int: Operation completed and buildman should exit with exit code
351 Boards: Boards object to use
352 """
353 brds = boards.Boards()
354 nr_cpus = threads or multiprocessing.cpu_count()
355 if maintainer_check:
356 warnings = brds.build_board_list(jobs=nr_cpus)[1]
357 if warnings:
358 for warn in warnings:
359 print(warn, file=sys.stderr)
360 return 2
361 return 0
362
363 if not os.path.exists(output_dir):
364 os.makedirs(output_dir)
365 board_file = os.path.join(output_dir, 'boards.cfg')
366 if regen_board_list and regen_board_list != '-':
367 board_file = regen_board_list
368
369 okay = brds.ensure_board_list(board_file, nr_cpus, force=regen_board_list,
370 quiet=not verbose)
371 if regen_board_list:
372 return 0 if okay else 2
373 brds.read_boards(board_file)
374 return brds
375
376
Simon Glass0d4874f2023-07-19 17:48:39 -0600377def determine_boards(brds, args, col, opt_boards, exclude_list):
378 """Determine which boards to build
379
380 Each element of args and exclude can refer to a board name, arch or SoC
381
382 Args:
383 brds (Boards): Boards object
384 args (list of str): Arguments describing boards to build
385 col (Terminal.Color): Color object
386 opt_boards (list of str): Specific boards to build, or None for all
387 exclude_list (list of str): Arguments describing boards to exclude
388
389 Returns:
390 tuple:
391 list of Board: List of Board objects that are marked selected
392 why_selected: Dictionary where each key is a buildman argument
393 provided by the user, and the value is the list of boards
394 brought in by that argument. For example, 'arm' might bring
395 in 400 boards, so in this case the key would be 'arm' and
396 the value would be a list of board names.
397 board_warnings: List of warnings obtained from board selected
398 """
399 exclude = []
400 if exclude_list:
401 for arg in exclude_list:
402 exclude += arg.split(',')
403
404 if opt_boards:
405 requested_boards = []
406 for brd in opt_boards:
407 requested_boards += brd.split(',')
408 else:
409 requested_boards = None
410 why_selected, board_warnings = brds.select_boards(args, exclude,
411 requested_boards)
412 selected = brds.get_selected()
413 if not selected:
414 sys.exit(col.build(col.RED, 'No matching boards found'))
415 return selected, why_selected, board_warnings
416
417
Simon Glass168d7922023-07-19 17:48:47 -0600418def adjust_options(options, series, selected):
419 """Adjust options according to various constraints
420
421 Updates verbose, show_errors, threads, jobs and step
422
423 Args:
424 options (Options): Options object to adjust
425 series (Series): Series being built / summarised
426 selected (list of Board): List of Board objects that are marked
427 """
428 if not series and not options.dry_run:
429 options.verbose = True
430 if not options.summary:
431 options.show_errors = True
432
433 # By default we have one thread per CPU. But if there are not enough jobs
434 # we can have fewer threads and use a high '-j' value for make.
435 if options.threads is None:
436 options.threads = min(multiprocessing.cpu_count(), len(selected))
437 if not options.jobs:
438 options.jobs = max(1, (multiprocessing.cpu_count() +
439 len(selected) - 1) // len(selected))
440
441 if not options.step:
442 options.step = len(series.commits) - 1
443
Simon Glasse48b9462023-07-19 17:48:49 -0600444
445def setup_output_dir(output_dir, work_in_output, branch, no_subdirs, col,
446 clean_dir):
447 """Set up the output directory
448
449 Args:
450 output_dir (str): Output directory provided by the user, or None if none
451 work_in_output (bool): True to work in the output directory
452 branch (str): Name of branch to build, or None if none
453 no_subdirs (bool): True to put the output in the top-level output dir
454 clean_dir: Used for tests only, indicates that the existing output_dir
455 should be removed before starting the build
456
457 Returns:
458 str: Updated output directory pathname
459 """
460 if not output_dir:
461 if work_in_output:
462 sys.exit(col.build(col.RED, '-w requires that you specify -o'))
463 output_dir = '..'
464 if branch and not no_subdirs:
465 # As a special case allow the board directory to be placed in the
466 # output directory itself rather than any subdirectory.
467 dirname = branch.replace('/', '_')
468 output_dir = os.path.join(output_dir, dirname)
469 if clean_dir and os.path.exists(output_dir):
470 shutil.rmtree(output_dir)
471 return output_dir
472
473
Simon Glass9ef05b92023-07-19 17:48:30 -0600474def do_buildman(options, args, toolchains=None, make_func=None, brds=None,
475 clean_dir=False, test_thread_exceptions=False):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000476 """The main control code for buildman
477
478 Args:
479 options: Command line options object
480 args: Command line arguments (list of strings)
Simon Glassd4144e42014-09-05 19:00:13 -0600481 toolchains: Toolchains to use - this should be a Toolchains()
482 object. If None, then it will be created and scanned
483 make_func: Make function to use for the builder. This is called
484 to execute 'make'. If this is None, the normal function
485 will be used, which calls the 'make' tool with suitable
486 arguments. This setting is useful for tests.
Simon Glasscc2c0d12022-07-11 19:04:00 -0600487 brds: Boards() object to use, containing a list of available
Simon Glass823e60b2014-09-05 19:00:16 -0600488 boards. If this is None it will be created and scanned.
Simon Glass24993312021-04-11 16:27:25 +1200489 clean_dir: Used for tests only, indicates that the existing output_dir
490 should be removed before starting the build
Simon Glass8116c782021-04-11 16:27:27 +1200491 test_thread_exceptions: Uses for tests only, True to make the threads
492 raise an exception instead of reporting their result. This simulates
493 a failure in the code somewhere
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000494 """
Simon Glassb8be2bd2023-07-19 17:48:31 -0600495 # Used so testing can obtain the builder: pylint: disable=W0603
496 global TEST_BUILDER
Simon Glass883a3212014-09-05 19:00:18 -0600497
Simon Glass0157b182022-01-29 14:14:11 -0700498 gitutil.setup()
Simon Glass713bea32016-07-27 20:33:02 -0600499 col = terminal.Color()
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000500
Simon Glassd230c012023-07-19 17:48:33 -0600501 git_dir = os.path.join(options.git, '.git')
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000502
Simon Glassb8680642023-07-19 17:48:42 -0600503 toolchains = get_toolchains(toolchains, col, options.override_toolchain,
504 options.fetch_arch, options.list_tool_chains,
505 options.verbose)
Simon Glasse48b9462023-07-19 17:48:49 -0600506 output_dir = setup_output_dir(
507 options.output_dir, options.work_in_output, options.branch,
508 options.no_subdirs, col, clean_dir)
Simon Glasseb70a2c2020-04-09 15:08:51 -0600509
Simon Glass7c66ead2019-12-05 15:59:13 -0700510 # Work out what subset of the boards we are building
Simon Glasscc2c0d12022-07-11 19:04:00 -0600511 if not brds:
Simon Glass372b4452023-07-19 17:48:43 -0600512 brds = get_boards_obj(output_dir, options.regen_board_list,
Simon Glass180c7182023-07-19 17:48:41 -0600513 options.maintainer_check, options.threads,
514 options.verbose)
515 if isinstance(brds, int):
516 return brds
Simon Glass7c66ead2019-12-05 15:59:13 -0700517
Simon Glass0d4874f2023-07-19 17:48:39 -0600518 selected, why_selected, board_warnings = determine_boards(
519 brds, args, col, options.boards, options.exclude)
Simon Glass7c66ead2019-12-05 15:59:13 -0700520
Simon Glass4e9162d2020-03-18 09:42:47 -0600521 if options.print_prefix:
Simon Glass9ef05b92023-07-19 17:48:30 -0600522 err = show_toolchain_prefix(brds, toolchains)
Simon Glass57cb9d52019-12-05 15:59:14 -0700523 if err:
Simon Glass252ac582022-01-29 14:14:17 -0700524 sys.exit(col.build(col.RED, err))
Simon Glass57cb9d52019-12-05 15:59:14 -0700525 return 0
526
Simon Glass9df59e42023-07-19 17:48:40 -0600527 series = determine_series(selected, col, git_dir, options.count,
528 options.branch, options.work_in_output)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000529
Simon Glass168d7922023-07-19 17:48:47 -0600530 adjust_options(options, series, selected)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000531
Simon Glass168d7922023-07-19 17:48:47 -0600532 # For a dry run, just show our actions as a sanity check
533 if options.dry_run:
534 show_actions(series, why_selected, selected, output_dir, board_warnings,
535 options.step, options.threads, options.jobs,
536 options.verbose)
537 return 0
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000538
Simon Glassd9800692022-01-29 14:14:05 -0700539 gnu_make = command.output(os.path.join(options.git,
Simon Glass785f1542016-07-25 18:59:00 -0600540 'scripts/show-gnu-make'), raise_on_error=False).rstrip()
Masahiro Yamada99796922014-07-22 11:19:09 +0900541 if not gnu_make:
Masahiro Yamada31e21412014-08-16 00:59:26 +0900542 sys.exit('GNU Make not found')
Masahiro Yamada99796922014-07-22 11:19:09 +0900543
Tom Rinid7713ad2022-11-09 19:14:53 -0700544 allow_missing = get_allow_missing(options.allow_missing,
545 options.no_allow_missing, len(selected),
546 options.branch)
547
Simon Glass05c96b12014-12-01 17:33:52 -0700548 # Create a new builder with the selected options.
Simon Glassf0207d72023-07-19 17:48:37 -0600549
Simon Glass2b4806e2022-01-22 05:07:33 -0700550 adjust_cfg = cfgutil.convert_list_to_dict(options.adjust_cfg)
551
Simon Glassbfb708a2023-02-21 12:40:29 -0700552 # Drop LOCALVERSION_AUTO since it changes the version string on every commit
553 if options.reproducible_builds:
554 # If these are mentioned, leave the local version alone
555 if 'LOCALVERSION' in adjust_cfg or 'LOCALVERSION_AUTO' in adjust_cfg:
556 print('Not dropping LOCALVERSION_AUTO for reproducible build')
557 else:
558 adjust_cfg['LOCALVERSION_AUTO'] = '~'
559
Simon Glassd230c012023-07-19 17:48:33 -0600560 builder = Builder(toolchains, output_dir, git_dir,
Masahiro Yamada99796922014-07-22 11:19:09 +0900561 options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
Simon Glass5971ab52014-12-01 17:33:55 -0700562 show_unknown=options.show_unknown, step=options.step,
Simon Glassd2ce6582014-12-01 17:34:07 -0700563 no_subdirs=options.no_subdirs, full_path=options.full_path,
Stephen Warrenf79f1e02016-04-11 10:48:44 -0600564 verbose_build=options.verbose_build,
Simon Glasseb70a2c2020-04-09 15:08:51 -0600565 mrproper=options.mrproper,
Simon Glassb50113f2016-11-13 14:25:51 -0700566 per_board_out_dir=options.per_board_out_dir,
Simon Glassb464f8e2016-11-13 14:25:53 -0700567 config_only=options.config_only,
Daniel Schwierzeck2371d1b2018-01-26 16:31:05 +0100568 squash_config_y=not options.preserve_config_y,
Simon Glassd829f122020-03-18 09:42:42 -0600569 warnings_as_errors=options.warnings_as_errors,
Simon Glass8116c782021-04-11 16:27:27 +1200570 work_in_output=options.work_in_output,
Simon Glass2b4806e2022-01-22 05:07:33 -0700571 test_thread_exceptions=test_thread_exceptions,
Tom Rinid7713ad2022-11-09 19:14:53 -0700572 adjust_cfg=adjust_cfg,
Simon Glassbfb708a2023-02-21 12:40:29 -0700573 allow_missing=allow_missing, no_lto=options.no_lto,
574 reproducible_builds=options.reproducible_builds)
Simon Glassb8be2bd2023-07-19 17:48:31 -0600575 TEST_BUILDER = builder
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000576 builder.force_config_on_failure = not options.quick
Simon Glassd4144e42014-09-05 19:00:13 -0600577 if make_func:
578 builder.do_make = make_func
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000579
Simon Glassf0207d72023-07-19 17:48:37 -0600580 builder.force_build = options.force_build
581 builder.force_build_failures = options.force_build_failures
582 builder.force_reconfig = options.force_reconfig
583 builder.in_tree = options.in_tree
584
585 # Work out which boards to build
586 board_selected = brds.get_selected_dict()
587
588 if series:
589 commits = series.commits
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000590 else:
Simon Glassf0207d72023-07-19 17:48:37 -0600591 commits = None
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000592
Simon Glassf0207d72023-07-19 17:48:37 -0600593 if not options.ide:
594 tprint(get_action_summary(options.summary, commits, board_selected,
Simon Glass1d3a5a52023-07-19 17:48:45 -0600595 options.step, options.threads, options.jobs))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000596
Simon Glassf0207d72023-07-19 17:48:37 -0600597 # We can't show function sizes without board details at present
598 if options.show_bloat:
599 options.show_detail = True
600 builder.SetDisplayOptions(
601 options.show_errors, options.show_sizes, options.show_detail,
602 options.show_bloat, options.list_error_boards, options.show_config,
603 options.show_environment, options.filter_dtb_warnings,
604 options.filter_migration_warnings, options.ide)
605 if options.summary:
606 builder.ShowSummary(commits, board_selected)
607 else:
608 fail, warned, excs = builder.BuildBoards(
609 commits, board_selected, options.keep_outputs, options.verbose)
610 if excs:
611 return 102
612 if fail:
613 return 100
614 if warned and not options.ignore_warnings:
615 return 101
Simon Glass2c3deb92014-08-28 09:43:39 -0600616 return 0