blob: 3a9454cd793c0c6a85da65b20539c8d10dcff378 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassc05694f2013-04-03 11:07:16 +00002# Copyright (c) 2013 The Chromium OS Authors.
3#
Simon Glassc05694f2013-04-03 11:07:16 +00004
Simon Glassc1e1e1d2023-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 Glassc05694f2013-04-03 11:07:16 +000010import multiprocessing
11import os
Simon Glassa10ebe12014-09-05 19:00:18 -060012import shutil
Simon Glassc05694f2013-04-03 11:07:16 +000013import sys
14
Simon Glass20751d62022-07-11 19:04:03 -060015from buildman import boards
Simon Glassf0d9c102020-04-17 18:09:02 -060016from buildman import bsettings
Simon Glasse5650a82022-01-22 05:07:33 -070017from buildman import cfgutil
Simon Glassf0d9c102020-04-17 18:09:02 -060018from buildman import toolchain
19from buildman.builder import Builder
Simon Glassa997ea52020-04-17 18:09:04 -060020from patman import gitutil
21from patman import patchstream
Simon Glass131444f2023-02-23 18:18:04 -070022from u_boot_pylib import command
23from u_boot_pylib import terminal
Simon Glass131444f2023-02-23 18:18:04 -070024from u_boot_pylib.terminal import tprint
Simon Glassc05694f2013-04-03 11:07:16 +000025
Simon Glassaf0e29f2023-07-19 17:48:31 -060026TEST_BUILDER = None
27
Simon Glassc1e1e1d2023-07-19 17:48:30 -060028def get_plural(count):
Simon Glassc05694f2013-04-03 11:07:16 +000029 """Returns a plural 's' if count is not 1"""
30 return 's' if count != 1 else ''
31
Simon Glass14905d32023-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 Glassc05694f2013-04-03 11:07:16 +000050 """Return a string summarising the intended action.
51
Simon Glassf50a7282023-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 Glassc05694f2013-04-03 11:07:16 +000060 Returns:
61 Summary string.
62 """
Simon Glass14905d32023-07-19 17:49:00 -060063 if commit_count:
64 commit_str = f'{commit_count} commit{get_plural(commit_count)}'
Simon Glassd326ad72014-08-09 15:32:59 -060065 else:
66 commit_str = 'current source'
Simon Glassaf0e29f2023-07-19 17:48:31 -060067 msg = (f"{'Summary of' if is_summary else 'Building'} "
68 f'{commit_str} for {len(selected)} boards')
Simon Glassf50a7282023-07-19 17:48:45 -060069 msg += (f' ({threads} thread{get_plural(threads)}, '
70 f'{jobs} job{get_plural(jobs)} per thread)')
Simon Glassaf0e29f2023-07-19 17:48:31 -060071 return msg
Simon Glassc05694f2013-04-03 11:07:16 +000072
Simon Glassaf0e29f2023-07-19 17:48:31 -060073# pylint: disable=R0913
Simon Glass01c671d2023-07-19 17:48:46 -060074def show_actions(series, why_selected, boards_selected, output_dir,
75 board_warnings, step, threads, jobs, verbose):
Simon Glassc05694f2013-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 Glass6af145f2017-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 Glassc05694f2013-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 Glassf83559c2023-07-19 17:48:36 -060087 output_dir (str): Output directory for builder
Simon Glassd9eb9f02018-06-11 23:26:46 -060088 board_warnings: List of warnings obtained from board selected
Simon Glass01c671d2023-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 Glassc05694f2013-04-03 11:07:16 +000093 """
94 col = terminal.Color()
Simon Glassc78ed662019-10-31 07:42:53 -060095 print('Dry run, so not doing much. But I would do this:')
96 print()
Simon Glassd326ad72014-08-09 15:32:59 -060097 if series:
98 commits = series.commits
99 else:
100 commits = None
Simon Glass14905d32023-07-19 17:49:00 -0600101 print(get_action_summary(False, count_build_commits(commits, step),
102 boards_selected, threads, jobs))
Simon Glassf83559c2023-07-19 17:48:36 -0600103 print(f'Build directory: {output_dir}')
Simon Glassd326ad72014-08-09 15:32:59 -0600104 if commits:
Simon Glass01c671d2023-07-19 17:48:46 -0600105 for upto in range(0, len(series.commits), step):
Simon Glassd326ad72014-08-09 15:32:59 -0600106 commit = series.commits[upto]
Simon Glassf45d3742022-01-29 14:14:17 -0700107 print(' ', col.build(col.YELLOW, commit.hash[:8], bright=False), end=' ')
Simon Glassc78ed662019-10-31 07:42:53 -0600108 print(commit.subject)
109 print()
Simon Glassc05694f2013-04-03 11:07:16 +0000110 for arg in why_selected:
111 if arg != 'all':
Simon Glassaf0e29f2023-07-19 17:48:31 -0600112 print(arg, f': {len(why_selected[arg])} boards')
Simon Glass01c671d2023-07-19 17:48:46 -0600113 if verbose:
Simon Glassaf0e29f2023-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 Glassd9eb9f02018-06-11 23:26:46 -0600117 if board_warnings:
118 for warning in board_warnings:
Simon Glassf45d3742022-01-29 14:14:17 -0700119 print(col.build(col.YELLOW, warning))
Simon Glassc05694f2013-04-03 11:07:16 +0000120
Simon Glassc1e1e1d2023-07-19 17:48:30 -0600121def show_toolchain_prefix(brds, toolchains):
Simon Glass48ac42e2019-12-05 15:59:14 -0700122 """Show information about a the tool chain used by one or more boards
123
Simon Glass2df44be2020-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 Glass48ac42e2019-12-05 15:59:14 -0700126
127 Args:
128 boards: Boards object containing selected boards
129 toolchains: Toolchains object containing available toolchains
Simon Glass48ac42e2019-12-05 15:59:14 -0700130
131 Return:
132 None on success, string error message otherwise
133 """
Simon Glass127a2392022-07-11 19:04:02 -0600134 board_selected = brds.get_selected_dict()
Simon Glass48ac42e2019-12-05 15:59:14 -0700135 tc_set = set()
Simon Glass5df45222022-07-11 19:04:00 -0600136 for brd in board_selected.values():
Simon Glass48ac42e2019-12-05 15:59:14 -0700137 tc_set.add(toolchains.Select(brd.arch))
138 if len(tc_set) != 1:
Simon Glasseaf1be22023-07-19 17:48:56 -0600139 sys.exit('Supplied boards must share one toolchain')
Simon Glassaf0e29f2023-07-19 17:48:31 -0600140 tchain = tc_set.pop()
141 print(tchain.GetEnvArgs(toolchain.VAR_CROSS_COMPILE))
Simon Glass48ac42e2019-12-05 15:59:14 -0700142
Tom Rini93ebd462022-11-09 19:14:53 -0700143def get_allow_missing(opt_allow, opt_no_allow, num_selected, has_branch):
Simon Glassaf0e29f2023-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 Rini93ebd462022-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 Glass2183b842023-07-19 17:48:33 -0600175
Simon Glassfdbff802023-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 Glass100196e2023-07-19 17:48:40 -0600216def determine_series(selected, col, git_dir, count, branch, work_in_output):
Simon Glass2183b842023-07-19 17:48:33 -0600217 """Determine the series which is to be built, if any
218
Simon Glass1b140492023-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 Glass2183b842023-07-19 17:48:33 -0600222 Args:
Simon Glassf50a7282023-07-19 17:48:45 -0600223 selected (list of Board): List of Board objects that are marked
Simon Glass100196e2023-07-19 17:48:40 -0600224 selected
225 col (Terminal.Color): Color object to use
226 git_dir (str): Git directory to use, e.g. './.git'
Simon Glass2183b842023-07-19 17:48:33 -0600227 count (int): Number of commits in branch
Simon Glass2183b842023-07-19 17:48:33 -0600228 branch (str): Name of branch to build, or None if none
Simon Glass100196e2023-07-19 17:48:40 -0600229 work_in_output (bool): True to work in the output directory
Simon Glass2183b842023-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 Glass100196e2023-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 Glassfdbff802023-07-19 17:48:48 -0600249 count, has_range = count_commits(branch, count, col, git_dir)
Simon Glass100196e2023-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 Glass2183b842023-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 Glass1b140492023-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 Glass2183b842023-07-19 17:48:33 -0600278 else:
279 series = None
280 return series
281
282
Simon Glass6c4beca2023-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 Glass3bd8e302023-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 Glassabcc11d2023-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 Glass72f8bff2023-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 Glass70274872023-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 Glass31353f22023-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 Glass3b300c52023-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 Glassfdd750b2023-07-19 17:48:57 -0600491
Simon Glass927c8272023-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 Glassfdd750b2023-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 Glass927c8272023-07-19 17:48:54 -0600511 if not options.ide:
Simon Glass14905d32023-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 Glass927c8272023-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 Glass3b300c52023-07-19 17:48:49 -0600533
Simon Glassf7524f32023-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 Glassc1e1e1d2023-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 Glassc05694f2013-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 Glassed098bb2014-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 Glass5df45222022-07-11 19:04:00 -0600572 brds: Boards() object to use, containing a list of available
Simon Glasscbd36582014-09-05 19:00:16 -0600573 boards. If this is None it will be created and scanned.
Simon Glassa29b3ea2021-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 Glass9bf9a722021-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 Glassc05694f2013-04-03 11:07:16 +0000579 """
Simon Glassaf0e29f2023-07-19 17:48:31 -0600580 # Used so testing can obtain the builder: pylint: disable=W0603
581 global TEST_BUILDER
Simon Glassa10ebe12014-09-05 19:00:18 -0600582
Simon Glass761648b2022-01-29 14:14:11 -0700583 gitutil.setup()
Simon Glass9f1ba0f2016-07-27 20:33:02 -0600584 col = terminal.Color()
Simon Glassc05694f2013-04-03 11:07:16 +0000585
Simon Glass2183b842023-07-19 17:48:33 -0600586 git_dir = os.path.join(options.git, '.git')
Simon Glassc05694f2013-04-03 11:07:16 +0000587
Simon Glass3bd8e302023-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 Glass3b300c52023-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 Glass6029af12020-04-09 15:08:51 -0600594
Simon Glassc05694f2013-04-03 11:07:16 +0000595 # Work out what subset of the boards we are building
Simon Glass5df45222022-07-11 19:04:00 -0600596 if not brds:
Simon Glass15349342023-07-19 17:48:43 -0600597 brds = get_boards_obj(output_dir, options.regen_board_list,
Simon Glassabcc11d2023-07-19 17:48:41 -0600598 options.maintainer_check, options.threads,
599 options.verbose)
600 if isinstance(brds, int):
601 return brds
Simon Glass924c73a2014-08-28 09:43:41 -0600602
Simon Glass72f8bff2023-07-19 17:48:39 -0600603 selected, why_selected, board_warnings = determine_boards(
604 brds, args, col, options.boards, options.exclude)
Simon Glassc05694f2013-04-03 11:07:16 +0000605
Simon Glass2df44be2020-03-18 09:42:47 -0600606 if options.print_prefix:
Simon Glasseaf1be22023-07-19 17:48:56 -0600607 show_toolchain_prefix(brds, toolchains)
Simon Glass48ac42e2019-12-05 15:59:14 -0700608 return 0
609
Simon Glass100196e2023-07-19 17:48:40 -0600610 series = determine_series(selected, col, git_dir, options.count,
611 options.branch, options.work_in_output)
Simon Glassc05694f2013-04-03 11:07:16 +0000612
Simon Glass70274872023-07-19 17:48:47 -0600613 adjust_options(options, series, selected)
Simon Glassc05694f2013-04-03 11:07:16 +0000614
Simon Glass70274872023-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 Glassc05694f2013-04-03 11:07:16 +0000621
Simon Glasscf91d312023-07-19 17:48:52 -0600622 # Create a new builder with the selected options
Simon Glass2183b842023-07-19 17:48:33 -0600623 builder = Builder(toolchains, output_dir, git_dir,
Simon Glassfdd750b2023-07-19 17:48:57 -0600624 options.threads, options.jobs, checkout=True,
Simon Glasse87bde12014-12-01 17:33:55 -0700625 show_unknown=options.show_unknown, step=options.step,
Simon Glass655b6102014-12-01 17:34:07 -0700626 no_subdirs=options.no_subdirs, full_path=options.full_path,
Stephen Warren97c96902016-04-11 10:48:44 -0600627 verbose_build=options.verbose_build,
Simon Glass6029af12020-04-09 15:08:51 -0600628 mrproper=options.mrproper,
Simon Glass739e8512016-11-13 14:25:51 -0700629 per_board_out_dir=options.per_board_out_dir,
Simon Glasscde5c302016-11-13 14:25:53 -0700630 config_only=options.config_only,
Daniel Schwierzeck20e2ea92018-01-26 16:31:05 +0100631 squash_config_y=not options.preserve_config_y,
Simon Glassb6eb8cf2020-03-18 09:42:42 -0600632 warnings_as_errors=options.warnings_as_errors,
Simon Glass9bf9a722021-04-11 16:27:27 +1200633 work_in_output=options.work_in_output,
Simon Glasse5650a82022-01-22 05:07:33 -0700634 test_thread_exceptions=test_thread_exceptions,
Simon Glassf7524f32023-07-19 17:48:58 -0600635 adjust_cfg=calc_adjust_cfg(options.adjust_cfg,
636 options.reproducible_builds),
Simon Glassb53fa062023-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 Glasscf91d312023-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 Glassc05694f2013-04-03 11:07:16 +0000646
Simon Glasscf91d312023-07-19 17:48:52 -0600647 TEST_BUILDER = builder
Simon Glassc05694f2013-04-03 11:07:16 +0000648
Simon Glass00401d92023-07-19 17:48:55 -0600649 return run_builder(builder, series.commits if series else None,
650 brds.get_selected_dict(), options)