blob: 3a9454cd793c0c6a85da65b20539c8d10dcff378 [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 Glass1d0c55d2023-07-19 17:49:00 -060032
33def count_build_commits(commits, step):
34 """Calculate the number of commits to be built
35
36 Args:
37 commits (list of Commit): Commits to build or None
38 step (int): Step value for commits, typically 1
39
40 Returns:
41 Number of commits that will be built
42 """
43 if commits:
44 count = len(commits)
45 return (count + step - 1) // step
46 return 0
47
48
49def get_action_summary(is_summary, commit_count, selected, threads, jobs):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000050 """Return a string summarising the intended action.
51
Simon Glass1d3a5a52023-07-19 17:48:45 -060052 Args:
53 is_summary (bool): True if this is a summary (otherwise it is building)
54 commits (list): List of commits being built
55 selected (list of Board): List of Board objects that are marked
56 step (int): Step increment through commits
57 threads (int): Number of processor threads being used
58 jobs (int): Number of jobs to build at once
59
Simon Glassfc3fe1c2013-04-03 11:07:16 +000060 Returns:
61 Summary string.
62 """
Simon Glass1d0c55d2023-07-19 17:49:00 -060063 if commit_count:
64 commit_str = f'{commit_count} commit{get_plural(commit_count)}'
Simon Glassfea58582014-08-09 15:32:59 -060065 else:
66 commit_str = 'current source'
Simon Glassb8be2bd2023-07-19 17:48:31 -060067 msg = (f"{'Summary of' if is_summary else 'Building'} "
68 f'{commit_str} for {len(selected)} boards')
Simon Glass1d3a5a52023-07-19 17:48:45 -060069 msg += (f' ({threads} thread{get_plural(threads)}, '
70 f'{jobs} job{get_plural(jobs)} per thread)')
Simon Glassb8be2bd2023-07-19 17:48:31 -060071 return msg
Simon Glassfc3fe1c2013-04-03 11:07:16 +000072
Simon Glassb8be2bd2023-07-19 17:48:31 -060073# pylint: disable=R0913
Simon Glass1b820ee2023-07-19 17:48:46 -060074def show_actions(series, why_selected, boards_selected, output_dir,
75 board_warnings, step, threads, jobs, verbose):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000076 """Display a list of actions that we would take, if not a dry run.
77
78 Args:
79 series: Series object
80 why_selected: Dictionary where each key is a buildman argument
Simon Glass8d7523c2017-01-23 05:38:56 -070081 provided by the user, and the value is the list of boards
82 brought in by that argument. For example, 'arm' might bring
83 in 400 boards, so in this case the key would be 'arm' and
Simon Glassfc3fe1c2013-04-03 11:07:16 +000084 the value would be a list of board names.
85 boards_selected: Dict of selected boards, key is target name,
86 value is Board object
Simon Glassd233dfb2023-07-19 17:48:36 -060087 output_dir (str): Output directory for builder
Simon Glass06890362018-06-11 23:26:46 -060088 board_warnings: List of warnings obtained from board selected
Simon Glass1b820ee2023-07-19 17:48:46 -060089 step (int): Step increment through commits
90 threads (int): Number of processor threads being used
91 jobs (int): Number of jobs to build at once
92 verbose (bool): True to indicate why each board was selected
Simon Glassfc3fe1c2013-04-03 11:07:16 +000093 """
94 col = terminal.Color()
Simon Glassc05aa032019-10-31 07:42:53 -060095 print('Dry run, so not doing much. But I would do this:')
96 print()
Simon Glassfea58582014-08-09 15:32:59 -060097 if series:
98 commits = series.commits
99 else:
100 commits = None
Simon Glass1d0c55d2023-07-19 17:49:00 -0600101 print(get_action_summary(False, count_build_commits(commits, step),
102 boards_selected, threads, jobs))
Simon Glassd233dfb2023-07-19 17:48:36 -0600103 print(f'Build directory: {output_dir}')
Simon Glassfea58582014-08-09 15:32:59 -0600104 if commits:
Simon Glass1b820ee2023-07-19 17:48:46 -0600105 for upto in range(0, len(series.commits), step):
Simon Glassfea58582014-08-09 15:32:59 -0600106 commit = series.commits[upto]
Simon Glass252ac582022-01-29 14:14:17 -0700107 print(' ', col.build(col.YELLOW, commit.hash[:8], bright=False), end=' ')
Simon Glassc05aa032019-10-31 07:42:53 -0600108 print(commit.subject)
109 print()
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000110 for arg in why_selected:
111 if arg != 'all':
Simon Glassb8be2bd2023-07-19 17:48:31 -0600112 print(arg, f': {len(why_selected[arg])} boards')
Simon Glass1b820ee2023-07-19 17:48:46 -0600113 if verbose:
Simon Glassb8be2bd2023-07-19 17:48:31 -0600114 print(f" {' '.join(why_selected[arg])}")
115 print('Total boards to build for each '
116 f"commit: {len(why_selected['all'])}\n")
Simon Glass06890362018-06-11 23:26:46 -0600117 if board_warnings:
118 for warning in board_warnings:
Simon Glass252ac582022-01-29 14:14:17 -0700119 print(col.build(col.YELLOW, warning))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000120
Simon Glass9ef05b92023-07-19 17:48:30 -0600121def show_toolchain_prefix(brds, toolchains):
Simon Glass57cb9d52019-12-05 15:59:14 -0700122 """Show information about a the tool chain used by one or more boards
123
Simon Glass4e9162d2020-03-18 09:42:47 -0600124 The function checks that all boards use the same toolchain, then prints
125 the correct value for CROSS_COMPILE.
Simon Glass57cb9d52019-12-05 15:59:14 -0700126
127 Args:
128 boards: Boards object containing selected boards
129 toolchains: Toolchains object containing available toolchains
Simon Glass57cb9d52019-12-05 15:59:14 -0700130
131 Return:
132 None on success, string error message otherwise
133 """
Simon Glass6014db62022-07-11 19:04:02 -0600134 board_selected = brds.get_selected_dict()
Simon Glass57cb9d52019-12-05 15:59:14 -0700135 tc_set = set()
Simon Glasscc2c0d12022-07-11 19:04:00 -0600136 for brd in board_selected.values():
Simon Glass57cb9d52019-12-05 15:59:14 -0700137 tc_set.add(toolchains.Select(brd.arch))
138 if len(tc_set) != 1:
Simon Glassea782332023-07-19 17:48:56 -0600139 sys.exit('Supplied boards must share one toolchain')
Simon Glassb8be2bd2023-07-19 17:48:31 -0600140 tchain = tc_set.pop()
141 print(tchain.GetEnvArgs(toolchain.VAR_CROSS_COMPILE))
Simon Glass57cb9d52019-12-05 15:59:14 -0700142
Tom Rinid7713ad2022-11-09 19:14:53 -0700143def get_allow_missing(opt_allow, opt_no_allow, num_selected, has_branch):
Simon Glassb8be2bd2023-07-19 17:48:31 -0600144 """Figure out whether to allow external blobs
145
146 Uses the allow-missing setting and the provided arguments to decide whether
147 missing external blobs should be allowed
148
149 Args:
150 opt_allow (bool): True if --allow-missing flag is set
151 opt_no_allow (bool): True if --no-allow-missing flag is set
152 num_selected (int): Number of selected board
153 has_branch (bool): True if a git branch (to build) has been provided
154
155 Returns:
156 bool: True to allow missing external blobs, False to produce an error if
157 external blobs are used
158 """
Tom Rinid7713ad2022-11-09 19:14:53 -0700159 allow_missing = False
160 am_setting = bsettings.GetGlobalItemValue('allow-missing')
161 if am_setting:
162 if am_setting == 'always':
163 allow_missing = True
164 if 'multiple' in am_setting and num_selected > 1:
165 allow_missing = True
166 if 'branch' in am_setting and has_branch:
167 allow_missing = True
168
169 if opt_allow:
170 allow_missing = True
171 if opt_no_allow:
172 allow_missing = False
173 return allow_missing
174
Simon Glassd230c012023-07-19 17:48:33 -0600175
Simon Glassaeb23812023-07-19 17:48:48 -0600176def count_commits(branch, count, col, git_dir):
177 """Could the number of commits in the branch/ranch being built
178
179 Args:
180 branch (str): Name of branch to build, or None if none
181 count (int): Number of commits to build, or -1 for all
182 col (Terminal.Color): Color object to use
183 git_dir (str): Git directory to use, e.g. './.git'
184
185 Returns:
186 tuple:
187 Number of commits being built
188 True if the 'branch' string contains a range rather than a simple
189 name
190 """
191 has_range = branch and '..' in branch
192 if count == -1:
193 if not branch:
194 count = 1
195 else:
196 if has_range:
197 count, msg = gitutil.count_commits_in_range(git_dir, branch)
198 else:
199 count, msg = gitutil.count_commits_in_branch(git_dir, branch)
200 if count is None:
201 sys.exit(col.build(col.RED, msg))
202 elif count == 0:
203 sys.exit(col.build(col.RED,
204 f"Range '{branch}' has no commits"))
205 if msg:
206 print(col.build(col.YELLOW, msg))
207 count += 1 # Build upstream commit also
208
209 if not count:
210 msg = (f"No commits found to process in branch '{branch}': "
211 "set branch's upstream or use -c flag")
212 sys.exit(col.build(col.RED, msg))
213 return count, has_range
214
215
Simon Glass9df59e42023-07-19 17:48:40 -0600216def determine_series(selected, col, git_dir, count, branch, work_in_output):
Simon Glassd230c012023-07-19 17:48:33 -0600217 """Determine the series which is to be built, if any
218
Simon Glass6378bad2023-07-19 17:48:50 -0600219 If there is a series, the commits in that series are numbered by setting
220 their sequence value (starting from 0). This is used by tests.
221
Simon Glassd230c012023-07-19 17:48:33 -0600222 Args:
Simon Glass1d3a5a52023-07-19 17:48:45 -0600223 selected (list of Board): List of Board objects that are marked
Simon Glass9df59e42023-07-19 17:48:40 -0600224 selected
225 col (Terminal.Color): Color object to use
Simon Glassd230c012023-07-19 17:48:33 -0600226 git_dir (str): Git directory to use, e.g. './.git'
Simon Glass9df59e42023-07-19 17:48:40 -0600227 count (int): Number of commits in branch
228 branch (str): Name of branch to build, or None if none
229 work_in_output (bool): True to work in the output directory
Simon Glassd230c012023-07-19 17:48:33 -0600230
231 Returns:
232 Series: Series to build, or None for none
233
234 Read the metadata from the commits. First look at the upstream commit,
235 then the ones in the branch. We would like to do something like
236 upstream/master~..branch but that isn't possible if upstream/master is
237 a merge commit (it will list all the commits that form part of the
238 merge)
239
240 Conflicting tags are not a problem for buildman, since it does not use
241 them. For example, Series-version is not useful for buildman. On the
242 other hand conflicting tags will cause an error. So allow later tags
243 to overwrite earlier ones by setting allow_overwrite=True
244 """
Simon Glass9df59e42023-07-19 17:48:40 -0600245
246 # Work out how many commits to build. We want to build everything on the
247 # branch. We also build the upstream commit as a control so we can see
248 # problems introduced by the first commit on the branch.
Simon Glassaeb23812023-07-19 17:48:48 -0600249 count, has_range = count_commits(branch, count, col, git_dir)
Simon Glass9df59e42023-07-19 17:48:40 -0600250 if work_in_output:
251 if len(selected) != 1:
252 sys.exit(col.build(col.RED,
253 '-w can only be used with a single board'))
254 if count != 1:
255 sys.exit(col.build(col.RED,
256 '-w can only be used with a single commit'))
257
Simon Glassd230c012023-07-19 17:48:33 -0600258 if branch:
259 if count == -1:
260 if has_range:
261 range_expr = branch
262 else:
263 range_expr = gitutil.get_range_in_branch(git_dir, branch)
264 upstream_commit = gitutil.get_upstream(git_dir, branch)
265 series = patchstream.get_metadata_for_list(upstream_commit,
266 git_dir, 1, series=None, allow_overwrite=True)
267
268 series = patchstream.get_metadata_for_list(range_expr,
269 git_dir, None, series, allow_overwrite=True)
270 else:
271 # Honour the count
272 series = patchstream.get_metadata_for_list(branch,
273 git_dir, count, series=None, allow_overwrite=True)
Simon Glass6378bad2023-07-19 17:48:50 -0600274
275 # Number the commits for test purposes
276 for i, commit in enumerate(series.commits):
277 commit.sequence = i
Simon Glassd230c012023-07-19 17:48:33 -0600278 else:
279 series = None
280 return series
281
282
Simon Glassf7a36d52023-07-19 17:48:34 -0600283def do_fetch_arch(toolchains, col, fetch_arch):
284 """Handle the --fetch-arch option
285
286 Args:
287 toolchains (Toolchains): Tool chains to use
288 col (terminal.Color): Color object to build
289 fetch_arch (str): Argument passed to the --fetch-arch option
290
291 Returns:
292 int: Return code for buildman
293 """
294 if fetch_arch == 'list':
295 sorted_list = toolchains.ListArchs()
296 print(col.build(
297 col.BLUE,
298 f"Available architectures: {' '.join(sorted_list)}\n"))
299 return 0
300
301 if fetch_arch == 'all':
302 fetch_arch = ','.join(toolchains.ListArchs())
303 print(col.build(col.CYAN,
304 f'\nDownloading toolchains: {fetch_arch}'))
305 for arch in fetch_arch.split(','):
306 print()
307 ret = toolchains.FetchAndInstall(arch)
308 if ret:
309 return ret
310 return 0
311
312
Simon Glassb8680642023-07-19 17:48:42 -0600313def get_toolchains(toolchains, col, override_toolchain, fetch_arch,
314 list_tool_chains, verbose):
315 """Get toolchains object to use
316
317 Args:
318 toolchains (Toolchains or None): Toolchains to use. If None, then a
319 Toolchains object will be created and scanned
320 col (Terminal.Color): Color object
321 override_toolchain (str or None): Override value for toolchain, or None
322 fetch_arch (bool): True to fetch the toolchain for the architectures
323 list_tool_chains (bool): True to list all tool chains
324 verbose (bool): True for verbose output when listing toolchains
325
326 Returns:
327 Either:
328 int: Operation completed and buildman should exit with exit code
329 Toolchains: Toolchains object to use
330 """
331 no_toolchains = toolchains is None
332 if no_toolchains:
333 toolchains = toolchain.Toolchains(override_toolchain)
334
335 if fetch_arch:
336 return do_fetch_arch(toolchains, col, fetch_arch)
337
338 if no_toolchains:
339 toolchains.GetSettings()
340 toolchains.Scan(list_tool_chains and verbose)
341 if list_tool_chains:
342 toolchains.List()
343 print()
344 return 0
345 return toolchains
346
347
Simon Glass180c7182023-07-19 17:48:41 -0600348def get_boards_obj(output_dir, regen_board_list, maintainer_check, threads,
349 verbose):
350 """Object the Boards object to use
351
352 Creates the output directory and ensures there is a boards.cfg file, then
353 read it in.
354
355 Args:
356 output_dir (str): Output directory to use
357 regen_board_list (bool): True to just regenerate the board list
358 maintainer_check (bool): True to just run a maintainer check
359 threads (int or None): Number of threads to use to create boards file
360 verbose (bool): False to suppress output from boards-file generation
361
362 Returns:
363 Either:
364 int: Operation completed and buildman should exit with exit code
365 Boards: Boards object to use
366 """
367 brds = boards.Boards()
368 nr_cpus = threads or multiprocessing.cpu_count()
369 if maintainer_check:
370 warnings = brds.build_board_list(jobs=nr_cpus)[1]
371 if warnings:
372 for warn in warnings:
373 print(warn, file=sys.stderr)
374 return 2
375 return 0
376
377 if not os.path.exists(output_dir):
378 os.makedirs(output_dir)
379 board_file = os.path.join(output_dir, 'boards.cfg')
380 if regen_board_list and regen_board_list != '-':
381 board_file = regen_board_list
382
383 okay = brds.ensure_board_list(board_file, nr_cpus, force=regen_board_list,
384 quiet=not verbose)
385 if regen_board_list:
386 return 0 if okay else 2
387 brds.read_boards(board_file)
388 return brds
389
390
Simon Glass0d4874f2023-07-19 17:48:39 -0600391def determine_boards(brds, args, col, opt_boards, exclude_list):
392 """Determine which boards to build
393
394 Each element of args and exclude can refer to a board name, arch or SoC
395
396 Args:
397 brds (Boards): Boards object
398 args (list of str): Arguments describing boards to build
399 col (Terminal.Color): Color object
400 opt_boards (list of str): Specific boards to build, or None for all
401 exclude_list (list of str): Arguments describing boards to exclude
402
403 Returns:
404 tuple:
405 list of Board: List of Board objects that are marked selected
406 why_selected: Dictionary where each key is a buildman argument
407 provided by the user, and the value is the list of boards
408 brought in by that argument. For example, 'arm' might bring
409 in 400 boards, so in this case the key would be 'arm' and
410 the value would be a list of board names.
411 board_warnings: List of warnings obtained from board selected
412 """
413 exclude = []
414 if exclude_list:
415 for arg in exclude_list:
416 exclude += arg.split(',')
417
418 if opt_boards:
419 requested_boards = []
420 for brd in opt_boards:
421 requested_boards += brd.split(',')
422 else:
423 requested_boards = None
424 why_selected, board_warnings = brds.select_boards(args, exclude,
425 requested_boards)
426 selected = brds.get_selected()
427 if not selected:
428 sys.exit(col.build(col.RED, 'No matching boards found'))
429 return selected, why_selected, board_warnings
430
431
Simon Glass168d7922023-07-19 17:48:47 -0600432def adjust_options(options, series, selected):
433 """Adjust options according to various constraints
434
435 Updates verbose, show_errors, threads, jobs and step
436
437 Args:
438 options (Options): Options object to adjust
439 series (Series): Series being built / summarised
440 selected (list of Board): List of Board objects that are marked
441 """
442 if not series and not options.dry_run:
443 options.verbose = True
444 if not options.summary:
445 options.show_errors = True
446
447 # By default we have one thread per CPU. But if there are not enough jobs
448 # we can have fewer threads and use a high '-j' value for make.
449 if options.threads is None:
450 options.threads = min(multiprocessing.cpu_count(), len(selected))
451 if not options.jobs:
452 options.jobs = max(1, (multiprocessing.cpu_count() +
453 len(selected) - 1) // len(selected))
454
455 if not options.step:
456 options.step = len(series.commits) - 1
457
Simon Glass4ec76822023-07-19 17:48:53 -0600458 # We can't show function sizes without board details at present
459 if options.show_bloat:
460 options.show_detail = True
461
Simon Glasse48b9462023-07-19 17:48:49 -0600462
463def setup_output_dir(output_dir, work_in_output, branch, no_subdirs, col,
464 clean_dir):
465 """Set up the output directory
466
467 Args:
468 output_dir (str): Output directory provided by the user, or None if none
469 work_in_output (bool): True to work in the output directory
470 branch (str): Name of branch to build, or None if none
471 no_subdirs (bool): True to put the output in the top-level output dir
472 clean_dir: Used for tests only, indicates that the existing output_dir
473 should be removed before starting the build
474
475 Returns:
476 str: Updated output directory pathname
477 """
478 if not output_dir:
479 if work_in_output:
480 sys.exit(col.build(col.RED, '-w requires that you specify -o'))
481 output_dir = '..'
482 if branch and not no_subdirs:
483 # As a special case allow the board directory to be placed in the
484 # output directory itself rather than any subdirectory.
485 dirname = branch.replace('/', '_')
486 output_dir = os.path.join(output_dir, dirname)
487 if clean_dir and os.path.exists(output_dir):
488 shutil.rmtree(output_dir)
489 return output_dir
490
Simon Glassa659b8d2023-07-19 17:48:57 -0600491
Simon Glass68f917c2023-07-19 17:48:54 -0600492def run_builder(builder, commits, board_selected, options):
493 """Run the builder or show the summary
494
495 Args:
496 commits (list of Commit): List of commits being built, None if no branch
497 boards_selected (dict): Dict of selected boards:
498 key: target name
499 value: Board object
500 options (Options): Options to use
501
502 Returns:
503 int: Return code for buildman
504 """
Simon Glassa659b8d2023-07-19 17:48:57 -0600505 gnu_make = command.output(os.path.join(options.git,
506 'scripts/show-gnu-make'), raise_on_error=False).rstrip()
507 if not gnu_make:
508 sys.exit('GNU Make not found')
509 builder.gnu_make = gnu_make
510
Simon Glass68f917c2023-07-19 17:48:54 -0600511 if not options.ide:
Simon Glass1d0c55d2023-07-19 17:49:00 -0600512 commit_count = count_build_commits(commits, options.step)
513 tprint(get_action_summary(options.summary, commit_count, board_selected,
514 options.threads, options.jobs))
Simon Glass68f917c2023-07-19 17:48:54 -0600515
516 builder.SetDisplayOptions(
517 options.show_errors, options.show_sizes, options.show_detail,
518 options.show_bloat, options.list_error_boards, options.show_config,
519 options.show_environment, options.filter_dtb_warnings,
520 options.filter_migration_warnings, options.ide)
521 if options.summary:
522 builder.ShowSummary(commits, board_selected)
523 else:
524 fail, warned, excs = builder.BuildBoards(
525 commits, board_selected, options.keep_outputs, options.verbose)
526 if excs:
527 return 102
528 if fail:
529 return 100
530 if warned and not options.ignore_warnings:
531 return 101
532 return 0
Simon Glasse48b9462023-07-19 17:48:49 -0600533
Simon Glass75584e12023-07-19 17:48:58 -0600534
535def calc_adjust_cfg(adjust_cfg, reproducible_builds):
536 """Calculate the value to use for adjust_cfg
537
538 Args:
539 adjust_cfg (list of str): List of configuration changes. See cfgutil for
540 details
541 reproducible_builds (bool): True to adjust the configuration to get
542 reproduceable builds
543
544 Returns:
545 adjust_cfg (list of str): List of configuration changes
546 """
547 adjust_cfg = cfgutil.convert_list_to_dict(adjust_cfg)
548
549 # Drop LOCALVERSION_AUTO since it changes the version string on every commit
550 if reproducible_builds:
551 # If these are mentioned, leave the local version alone
552 if 'LOCALVERSION' in adjust_cfg or 'LOCALVERSION_AUTO' in adjust_cfg:
553 print('Not dropping LOCALVERSION_AUTO for reproducible build')
554 else:
555 adjust_cfg['LOCALVERSION_AUTO'] = '~'
556 return adjust_cfg
557
558
Simon Glass9ef05b92023-07-19 17:48:30 -0600559def do_buildman(options, args, toolchains=None, make_func=None, brds=None,
560 clean_dir=False, test_thread_exceptions=False):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000561 """The main control code for buildman
562
563 Args:
564 options: Command line options object
565 args: Command line arguments (list of strings)
Simon Glassd4144e42014-09-05 19:00:13 -0600566 toolchains: Toolchains to use - this should be a Toolchains()
567 object. If None, then it will be created and scanned
568 make_func: Make function to use for the builder. This is called
569 to execute 'make'. If this is None, the normal function
570 will be used, which calls the 'make' tool with suitable
571 arguments. This setting is useful for tests.
Simon Glasscc2c0d12022-07-11 19:04:00 -0600572 brds: Boards() object to use, containing a list of available
Simon Glass823e60b2014-09-05 19:00:16 -0600573 boards. If this is None it will be created and scanned.
Simon Glass24993312021-04-11 16:27:25 +1200574 clean_dir: Used for tests only, indicates that the existing output_dir
575 should be removed before starting the build
Simon Glass8116c782021-04-11 16:27:27 +1200576 test_thread_exceptions: Uses for tests only, True to make the threads
577 raise an exception instead of reporting their result. This simulates
578 a failure in the code somewhere
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000579 """
Simon Glassb8be2bd2023-07-19 17:48:31 -0600580 # Used so testing can obtain the builder: pylint: disable=W0603
581 global TEST_BUILDER
Simon Glass883a3212014-09-05 19:00:18 -0600582
Simon Glass0157b182022-01-29 14:14:11 -0700583 gitutil.setup()
Simon Glass713bea32016-07-27 20:33:02 -0600584 col = terminal.Color()
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000585
Simon Glassd230c012023-07-19 17:48:33 -0600586 git_dir = os.path.join(options.git, '.git')
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000587
Simon Glassb8680642023-07-19 17:48:42 -0600588 toolchains = get_toolchains(toolchains, col, options.override_toolchain,
589 options.fetch_arch, options.list_tool_chains,
590 options.verbose)
Simon Glasse48b9462023-07-19 17:48:49 -0600591 output_dir = setup_output_dir(
592 options.output_dir, options.work_in_output, options.branch,
593 options.no_subdirs, col, clean_dir)
Simon Glasseb70a2c2020-04-09 15:08:51 -0600594
Simon Glass7c66ead2019-12-05 15:59:13 -0700595 # Work out what subset of the boards we are building
Simon Glasscc2c0d12022-07-11 19:04:00 -0600596 if not brds:
Simon Glass372b4452023-07-19 17:48:43 -0600597 brds = get_boards_obj(output_dir, options.regen_board_list,
Simon Glass180c7182023-07-19 17:48:41 -0600598 options.maintainer_check, options.threads,
599 options.verbose)
600 if isinstance(brds, int):
601 return brds
Simon Glass7c66ead2019-12-05 15:59:13 -0700602
Simon Glass0d4874f2023-07-19 17:48:39 -0600603 selected, why_selected, board_warnings = determine_boards(
604 brds, args, col, options.boards, options.exclude)
Simon Glass7c66ead2019-12-05 15:59:13 -0700605
Simon Glass4e9162d2020-03-18 09:42:47 -0600606 if options.print_prefix:
Simon Glassea782332023-07-19 17:48:56 -0600607 show_toolchain_prefix(brds, toolchains)
Simon Glass57cb9d52019-12-05 15:59:14 -0700608 return 0
609
Simon Glass9df59e42023-07-19 17:48:40 -0600610 series = determine_series(selected, col, git_dir, options.count,
611 options.branch, options.work_in_output)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000612
Simon Glass168d7922023-07-19 17:48:47 -0600613 adjust_options(options, series, selected)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000614
Simon Glass168d7922023-07-19 17:48:47 -0600615 # For a dry run, just show our actions as a sanity check
616 if options.dry_run:
617 show_actions(series, why_selected, selected, output_dir, board_warnings,
618 options.step, options.threads, options.jobs,
619 options.verbose)
620 return 0
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000621
Simon Glassffd06d32023-07-19 17:48:52 -0600622 # Create a new builder with the selected options
Simon Glassd230c012023-07-19 17:48:33 -0600623 builder = Builder(toolchains, output_dir, git_dir,
Simon Glassa659b8d2023-07-19 17:48:57 -0600624 options.threads, options.jobs, checkout=True,
Simon Glass5971ab52014-12-01 17:33:55 -0700625 show_unknown=options.show_unknown, step=options.step,
Simon Glassd2ce6582014-12-01 17:34:07 -0700626 no_subdirs=options.no_subdirs, full_path=options.full_path,
Stephen Warrenf79f1e02016-04-11 10:48:44 -0600627 verbose_build=options.verbose_build,
Simon Glasseb70a2c2020-04-09 15:08:51 -0600628 mrproper=options.mrproper,
Simon Glassb50113f2016-11-13 14:25:51 -0700629 per_board_out_dir=options.per_board_out_dir,
Simon Glassb464f8e2016-11-13 14:25:53 -0700630 config_only=options.config_only,
Daniel Schwierzeck2371d1b2018-01-26 16:31:05 +0100631 squash_config_y=not options.preserve_config_y,
Simon Glassd829f122020-03-18 09:42:42 -0600632 warnings_as_errors=options.warnings_as_errors,
Simon Glass8116c782021-04-11 16:27:27 +1200633 work_in_output=options.work_in_output,
Simon Glass2b4806e2022-01-22 05:07:33 -0700634 test_thread_exceptions=test_thread_exceptions,
Simon Glass75584e12023-07-19 17:48:58 -0600635 adjust_cfg=calc_adjust_cfg(options.adjust_cfg,
636 options.reproducible_builds),
Simon Glassf6df5ed2023-07-19 17:48:59 -0600637 allow_missing=get_allow_missing(options.allow_missing,
638 options.no_allow_missing,
639 len(selected), options.branch),
640 no_lto=options.no_lto,
Simon Glassffd06d32023-07-19 17:48:52 -0600641 reproducible_builds=options.reproducible_builds,
642 force_build = options.force_build,
643 force_build_failures = options.force_build_failures,
644 force_reconfig = options.force_reconfig, in_tree = options.in_tree,
645 force_config_on_failure=not options.quick, make_func=make_func)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000646
Simon Glassffd06d32023-07-19 17:48:52 -0600647 TEST_BUILDER = builder
Simon Glassf0207d72023-07-19 17:48:37 -0600648
Simon Glass985d7ae2023-07-19 17:48:55 -0600649 return run_builder(builder, series.commits if series else None,
650 brds.get_selected_dict(), options)