blob: 8f6850c5211357a84fc00084b0eb7850d5ed58cc [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
Simon Glassa8a0ce72023-07-19 17:49:28 -0600143def show_arch(brds):
144 """Show information about a the architecture used by one or more boards
145
146 The function checks that all boards use the same architecture, then prints
147 the correct value for ARCH.
148
149 Args:
150 boards: Boards object containing selected boards
151
152 Return:
153 None on success, string error message otherwise
154 """
155 board_selected = brds.get_selected_dict()
156 arch_set = set()
157 for brd in board_selected.values():
158 arch_set.add(brd.arch)
159 if len(arch_set) != 1:
160 sys.exit('Supplied boards must share one arch')
161 print(arch_set.pop())
162
Tom Rini93ebd462022-11-09 19:14:53 -0700163def get_allow_missing(opt_allow, opt_no_allow, num_selected, has_branch):
Simon Glassaf0e29f2023-07-19 17:48:31 -0600164 """Figure out whether to allow external blobs
165
166 Uses the allow-missing setting and the provided arguments to decide whether
167 missing external blobs should be allowed
168
169 Args:
170 opt_allow (bool): True if --allow-missing flag is set
171 opt_no_allow (bool): True if --no-allow-missing flag is set
172 num_selected (int): Number of selected board
173 has_branch (bool): True if a git branch (to build) has been provided
174
175 Returns:
176 bool: True to allow missing external blobs, False to produce an error if
177 external blobs are used
178 """
Tom Rini93ebd462022-11-09 19:14:53 -0700179 allow_missing = False
Simon Glass06b83a52023-07-19 17:49:05 -0600180 am_setting = bsettings.get_global_item_value('allow-missing')
Tom Rini93ebd462022-11-09 19:14:53 -0700181 if am_setting:
182 if am_setting == 'always':
183 allow_missing = True
184 if 'multiple' in am_setting and num_selected > 1:
185 allow_missing = True
186 if 'branch' in am_setting and has_branch:
187 allow_missing = True
188
189 if opt_allow:
190 allow_missing = True
191 if opt_no_allow:
192 allow_missing = False
193 return allow_missing
194
Simon Glass2183b842023-07-19 17:48:33 -0600195
Simon Glassfdbff802023-07-19 17:48:48 -0600196def count_commits(branch, count, col, git_dir):
197 """Could the number of commits in the branch/ranch being built
198
199 Args:
200 branch (str): Name of branch to build, or None if none
201 count (int): Number of commits to build, or -1 for all
202 col (Terminal.Color): Color object to use
203 git_dir (str): Git directory to use, e.g. './.git'
204
205 Returns:
206 tuple:
207 Number of commits being built
208 True if the 'branch' string contains a range rather than a simple
209 name
210 """
211 has_range = branch and '..' in branch
212 if count == -1:
213 if not branch:
214 count = 1
215 else:
216 if has_range:
217 count, msg = gitutil.count_commits_in_range(git_dir, branch)
218 else:
219 count, msg = gitutil.count_commits_in_branch(git_dir, branch)
220 if count is None:
221 sys.exit(col.build(col.RED, msg))
222 elif count == 0:
223 sys.exit(col.build(col.RED,
224 f"Range '{branch}' has no commits"))
225 if msg:
226 print(col.build(col.YELLOW, msg))
227 count += 1 # Build upstream commit also
228
229 if not count:
230 msg = (f"No commits found to process in branch '{branch}': "
231 "set branch's upstream or use -c flag")
232 sys.exit(col.build(col.RED, msg))
233 return count, has_range
234
235
Simon Glass100196e2023-07-19 17:48:40 -0600236def determine_series(selected, col, git_dir, count, branch, work_in_output):
Simon Glass2183b842023-07-19 17:48:33 -0600237 """Determine the series which is to be built, if any
238
Simon Glass1b140492023-07-19 17:48:50 -0600239 If there is a series, the commits in that series are numbered by setting
240 their sequence value (starting from 0). This is used by tests.
241
Simon Glass2183b842023-07-19 17:48:33 -0600242 Args:
Simon Glassf50a7282023-07-19 17:48:45 -0600243 selected (list of Board): List of Board objects that are marked
Simon Glass100196e2023-07-19 17:48:40 -0600244 selected
245 col (Terminal.Color): Color object to use
246 git_dir (str): Git directory to use, e.g. './.git'
Simon Glass2183b842023-07-19 17:48:33 -0600247 count (int): Number of commits in branch
Simon Glass2183b842023-07-19 17:48:33 -0600248 branch (str): Name of branch to build, or None if none
Simon Glass100196e2023-07-19 17:48:40 -0600249 work_in_output (bool): True to work in the output directory
Simon Glass2183b842023-07-19 17:48:33 -0600250
251 Returns:
252 Series: Series to build, or None for none
253
254 Read the metadata from the commits. First look at the upstream commit,
255 then the ones in the branch. We would like to do something like
256 upstream/master~..branch but that isn't possible if upstream/master is
257 a merge commit (it will list all the commits that form part of the
258 merge)
259
260 Conflicting tags are not a problem for buildman, since it does not use
261 them. For example, Series-version is not useful for buildman. On the
262 other hand conflicting tags will cause an error. So allow later tags
263 to overwrite earlier ones by setting allow_overwrite=True
264 """
Simon Glass100196e2023-07-19 17:48:40 -0600265
266 # Work out how many commits to build. We want to build everything on the
267 # branch. We also build the upstream commit as a control so we can see
268 # problems introduced by the first commit on the branch.
Simon Glassfdbff802023-07-19 17:48:48 -0600269 count, has_range = count_commits(branch, count, col, git_dir)
Simon Glass100196e2023-07-19 17:48:40 -0600270 if work_in_output:
271 if len(selected) != 1:
272 sys.exit(col.build(col.RED,
273 '-w can only be used with a single board'))
274 if count != 1:
275 sys.exit(col.build(col.RED,
276 '-w can only be used with a single commit'))
277
Simon Glass2183b842023-07-19 17:48:33 -0600278 if branch:
279 if count == -1:
280 if has_range:
281 range_expr = branch
282 else:
283 range_expr = gitutil.get_range_in_branch(git_dir, branch)
284 upstream_commit = gitutil.get_upstream(git_dir, branch)
285 series = patchstream.get_metadata_for_list(upstream_commit,
286 git_dir, 1, series=None, allow_overwrite=True)
287
288 series = patchstream.get_metadata_for_list(range_expr,
289 git_dir, None, series, allow_overwrite=True)
290 else:
291 # Honour the count
292 series = patchstream.get_metadata_for_list(branch,
293 git_dir, count, series=None, allow_overwrite=True)
Simon Glass1b140492023-07-19 17:48:50 -0600294
295 # Number the commits for test purposes
296 for i, commit in enumerate(series.commits):
297 commit.sequence = i
Simon Glass2183b842023-07-19 17:48:33 -0600298 else:
299 series = None
300 return series
301
302
Simon Glass6c4beca2023-07-19 17:48:34 -0600303def do_fetch_arch(toolchains, col, fetch_arch):
304 """Handle the --fetch-arch option
305
306 Args:
307 toolchains (Toolchains): Tool chains to use
308 col (terminal.Color): Color object to build
309 fetch_arch (str): Argument passed to the --fetch-arch option
310
311 Returns:
312 int: Return code for buildman
313 """
314 if fetch_arch == 'list':
315 sorted_list = toolchains.ListArchs()
316 print(col.build(
317 col.BLUE,
318 f"Available architectures: {' '.join(sorted_list)}\n"))
319 return 0
320
321 if fetch_arch == 'all':
322 fetch_arch = ','.join(toolchains.ListArchs())
323 print(col.build(col.CYAN,
324 f'\nDownloading toolchains: {fetch_arch}'))
325 for arch in fetch_arch.split(','):
326 print()
327 ret = toolchains.FetchAndInstall(arch)
328 if ret:
329 return ret
330 return 0
331
332
Simon Glass3bd8e302023-07-19 17:48:42 -0600333def get_toolchains(toolchains, col, override_toolchain, fetch_arch,
334 list_tool_chains, verbose):
335 """Get toolchains object to use
336
337 Args:
338 toolchains (Toolchains or None): Toolchains to use. If None, then a
339 Toolchains object will be created and scanned
340 col (Terminal.Color): Color object
341 override_toolchain (str or None): Override value for toolchain, or None
342 fetch_arch (bool): True to fetch the toolchain for the architectures
343 list_tool_chains (bool): True to list all tool chains
344 verbose (bool): True for verbose output when listing toolchains
345
346 Returns:
347 Either:
348 int: Operation completed and buildman should exit with exit code
349 Toolchains: Toolchains object to use
350 """
351 no_toolchains = toolchains is None
352 if no_toolchains:
353 toolchains = toolchain.Toolchains(override_toolchain)
354
355 if fetch_arch:
356 return do_fetch_arch(toolchains, col, fetch_arch)
357
358 if no_toolchains:
359 toolchains.GetSettings()
360 toolchains.Scan(list_tool_chains and verbose)
361 if list_tool_chains:
362 toolchains.List()
363 print()
364 return 0
365 return toolchains
366
367
Simon Glass66d4c882023-07-19 17:49:30 -0600368def get_boards_obj(output_dir, regen_board_list, maintainer_check, full_check,
369 threads, verbose):
Simon Glassabcc11d2023-07-19 17:48:41 -0600370 """Object the Boards object to use
371
372 Creates the output directory and ensures there is a boards.cfg file, then
373 read it in.
374
375 Args:
376 output_dir (str): Output directory to use
377 regen_board_list (bool): True to just regenerate the board list
378 maintainer_check (bool): True to just run a maintainer check
Simon Glass66d4c882023-07-19 17:49:30 -0600379 full_check (bool): True to just run a full check of Kconfig and
380 maintainers
Simon Glassabcc11d2023-07-19 17:48:41 -0600381 threads (int or None): Number of threads to use to create boards file
382 verbose (bool): False to suppress output from boards-file generation
383
384 Returns:
385 Either:
386 int: Operation completed and buildman should exit with exit code
387 Boards: Boards object to use
388 """
389 brds = boards.Boards()
390 nr_cpus = threads or multiprocessing.cpu_count()
Simon Glass66d4c882023-07-19 17:49:30 -0600391 if maintainer_check or full_check:
392 warnings = brds.build_board_list(jobs=nr_cpus,
393 warn_targets=full_check)[1]
Simon Glassabcc11d2023-07-19 17:48:41 -0600394 if warnings:
395 for warn in warnings:
396 print(warn, file=sys.stderr)
397 return 2
398 return 0
399
400 if not os.path.exists(output_dir):
401 os.makedirs(output_dir)
402 board_file = os.path.join(output_dir, 'boards.cfg')
403 if regen_board_list and regen_board_list != '-':
404 board_file = regen_board_list
405
406 okay = brds.ensure_board_list(board_file, nr_cpus, force=regen_board_list,
407 quiet=not verbose)
408 if regen_board_list:
409 return 0 if okay else 2
410 brds.read_boards(board_file)
411 return brds
412
413
Simon Glass72f8bff2023-07-19 17:48:39 -0600414def determine_boards(brds, args, col, opt_boards, exclude_list):
415 """Determine which boards to build
416
417 Each element of args and exclude can refer to a board name, arch or SoC
418
419 Args:
420 brds (Boards): Boards object
421 args (list of str): Arguments describing boards to build
422 col (Terminal.Color): Color object
423 opt_boards (list of str): Specific boards to build, or None for all
424 exclude_list (list of str): Arguments describing boards to exclude
425
426 Returns:
427 tuple:
428 list of Board: List of Board objects that are marked selected
429 why_selected: Dictionary where each key is a buildman argument
430 provided by the user, and the value is the list of boards
431 brought in by that argument. For example, 'arm' might bring
432 in 400 boards, so in this case the key would be 'arm' and
433 the value would be a list of board names.
434 board_warnings: List of warnings obtained from board selected
435 """
436 exclude = []
437 if exclude_list:
438 for arg in exclude_list:
439 exclude += arg.split(',')
440
441 if opt_boards:
442 requested_boards = []
443 for brd in opt_boards:
444 requested_boards += brd.split(',')
445 else:
446 requested_boards = None
447 why_selected, board_warnings = brds.select_boards(args, exclude,
448 requested_boards)
449 selected = brds.get_selected()
450 if not selected:
451 sys.exit(col.build(col.RED, 'No matching boards found'))
452 return selected, why_selected, board_warnings
453
454
Simon Glassa56ac992023-07-19 17:49:04 -0600455def adjust_args(args, series, selected):
456 """Adjust arguments according to various constraints
Simon Glass70274872023-07-19 17:48:47 -0600457
458 Updates verbose, show_errors, threads, jobs and step
459
460 Args:
Simon Glassa56ac992023-07-19 17:49:04 -0600461 args (Namespace): Namespace object to adjust
Simon Glass70274872023-07-19 17:48:47 -0600462 series (Series): Series being built / summarised
463 selected (list of Board): List of Board objects that are marked
464 """
Simon Glassa56ac992023-07-19 17:49:04 -0600465 if not series and not args.dry_run:
466 args.verbose = True
467 if not args.summary:
468 args.show_errors = True
Simon Glass70274872023-07-19 17:48:47 -0600469
470 # By default we have one thread per CPU. But if there are not enough jobs
471 # we can have fewer threads and use a high '-j' value for make.
Simon Glassa56ac992023-07-19 17:49:04 -0600472 if args.threads is None:
473 args.threads = min(multiprocessing.cpu_count(), len(selected))
474 if not args.jobs:
475 args.jobs = max(1, (multiprocessing.cpu_count() +
Simon Glass70274872023-07-19 17:48:47 -0600476 len(selected) - 1) // len(selected))
477
Simon Glassa56ac992023-07-19 17:49:04 -0600478 if not args.step:
479 args.step = len(series.commits) - 1
Simon Glass70274872023-07-19 17:48:47 -0600480
Simon Glass31353f22023-07-19 17:48:53 -0600481 # We can't show function sizes without board details at present
Simon Glassa56ac992023-07-19 17:49:04 -0600482 if args.show_bloat:
483 args.show_detail = True
Simon Glass31353f22023-07-19 17:48:53 -0600484
Simon Glass3b300c52023-07-19 17:48:49 -0600485
486def setup_output_dir(output_dir, work_in_output, branch, no_subdirs, col,
487 clean_dir):
488 """Set up the output directory
489
490 Args:
491 output_dir (str): Output directory provided by the user, or None if none
492 work_in_output (bool): True to work in the output directory
493 branch (str): Name of branch to build, or None if none
494 no_subdirs (bool): True to put the output in the top-level output dir
495 clean_dir: Used for tests only, indicates that the existing output_dir
496 should be removed before starting the build
497
498 Returns:
499 str: Updated output directory pathname
500 """
501 if not output_dir:
502 if work_in_output:
503 sys.exit(col.build(col.RED, '-w requires that you specify -o'))
504 output_dir = '..'
505 if branch and not no_subdirs:
506 # As a special case allow the board directory to be placed in the
507 # output directory itself rather than any subdirectory.
508 dirname = branch.replace('/', '_')
509 output_dir = os.path.join(output_dir, dirname)
510 if clean_dir and os.path.exists(output_dir):
511 shutil.rmtree(output_dir)
512 return output_dir
513
Simon Glassfdd750b2023-07-19 17:48:57 -0600514
Simon Glassa56ac992023-07-19 17:49:04 -0600515def run_builder(builder, commits, board_selected, args):
Simon Glass927c8272023-07-19 17:48:54 -0600516 """Run the builder or show the summary
517
518 Args:
519 commits (list of Commit): List of commits being built, None if no branch
520 boards_selected (dict): Dict of selected boards:
521 key: target name
522 value: Board object
Simon Glassa56ac992023-07-19 17:49:04 -0600523 args (Namespace): Namespace to use
Simon Glass927c8272023-07-19 17:48:54 -0600524
525 Returns:
526 int: Return code for buildman
527 """
Simon Glassa56ac992023-07-19 17:49:04 -0600528 gnu_make = command.output(os.path.join(args.git,
Simon Glassfdd750b2023-07-19 17:48:57 -0600529 'scripts/show-gnu-make'), raise_on_error=False).rstrip()
530 if not gnu_make:
531 sys.exit('GNU Make not found')
532 builder.gnu_make = gnu_make
533
Simon Glassa56ac992023-07-19 17:49:04 -0600534 if not args.ide:
535 commit_count = count_build_commits(commits, args.step)
536 tprint(get_action_summary(args.summary, commit_count, board_selected,
537 args.threads, args.jobs))
Simon Glass927c8272023-07-19 17:48:54 -0600538
Simon Glassbc74d942023-07-19 17:49:06 -0600539 builder.set_display_options(
540 args.show_errors, args.show_sizes, args.show_detail, args.show_bloat,
541 args.list_error_boards, args.show_config, args.show_environment,
542 args.filter_dtb_warnings, args.filter_migration_warnings, args.ide)
Simon Glassa56ac992023-07-19 17:49:04 -0600543 if args.summary:
Simon Glassbc74d942023-07-19 17:49:06 -0600544 builder.show_summary(commits, board_selected)
Simon Glass927c8272023-07-19 17:48:54 -0600545 else:
Simon Glassbc74d942023-07-19 17:49:06 -0600546 fail, warned, excs = builder.build_boards(
Simon Glassa56ac992023-07-19 17:49:04 -0600547 commits, board_selected, args.keep_outputs, args.verbose)
Simon Glass927c8272023-07-19 17:48:54 -0600548 if excs:
549 return 102
550 if fail:
551 return 100
Simon Glassa56ac992023-07-19 17:49:04 -0600552 if warned and not args.ignore_warnings:
Simon Glass927c8272023-07-19 17:48:54 -0600553 return 101
554 return 0
Simon Glass3b300c52023-07-19 17:48:49 -0600555
Simon Glassf7524f32023-07-19 17:48:58 -0600556
557def calc_adjust_cfg(adjust_cfg, reproducible_builds):
558 """Calculate the value to use for adjust_cfg
559
560 Args:
561 adjust_cfg (list of str): List of configuration changes. See cfgutil for
562 details
563 reproducible_builds (bool): True to adjust the configuration to get
564 reproduceable builds
565
566 Returns:
567 adjust_cfg (list of str): List of configuration changes
568 """
569 adjust_cfg = cfgutil.convert_list_to_dict(adjust_cfg)
570
571 # Drop LOCALVERSION_AUTO since it changes the version string on every commit
572 if reproducible_builds:
573 # If these are mentioned, leave the local version alone
574 if 'LOCALVERSION' in adjust_cfg or 'LOCALVERSION_AUTO' in adjust_cfg:
575 print('Not dropping LOCALVERSION_AUTO for reproducible build')
576 else:
577 adjust_cfg['LOCALVERSION_AUTO'] = '~'
578 return adjust_cfg
579
580
Simon Glassa56ac992023-07-19 17:49:04 -0600581def do_buildman(args, toolchains=None, make_func=None, brds=None,
Simon Glassc1e1e1d2023-07-19 17:48:30 -0600582 clean_dir=False, test_thread_exceptions=False):
Simon Glassc05694f2013-04-03 11:07:16 +0000583 """The main control code for buildman
584
585 Args:
Simon Glassa56ac992023-07-19 17:49:04 -0600586 args: ArgumentParser object
Simon Glassc05694f2013-04-03 11:07:16 +0000587 args: Command line arguments (list of strings)
Simon Glassed098bb2014-09-05 19:00:13 -0600588 toolchains: Toolchains to use - this should be a Toolchains()
589 object. If None, then it will be created and scanned
590 make_func: Make function to use for the builder. This is called
591 to execute 'make'. If this is None, the normal function
592 will be used, which calls the 'make' tool with suitable
593 arguments. This setting is useful for tests.
Simon Glass5df45222022-07-11 19:04:00 -0600594 brds: Boards() object to use, containing a list of available
Simon Glasscbd36582014-09-05 19:00:16 -0600595 boards. If this is None it will be created and scanned.
Simon Glassa29b3ea2021-04-11 16:27:25 +1200596 clean_dir: Used for tests only, indicates that the existing output_dir
597 should be removed before starting the build
Simon Glass9bf9a722021-04-11 16:27:27 +1200598 test_thread_exceptions: Uses for tests only, True to make the threads
599 raise an exception instead of reporting their result. This simulates
600 a failure in the code somewhere
Simon Glassc05694f2013-04-03 11:07:16 +0000601 """
Simon Glassaf0e29f2023-07-19 17:48:31 -0600602 # Used so testing can obtain the builder: pylint: disable=W0603
603 global TEST_BUILDER
Simon Glassa10ebe12014-09-05 19:00:18 -0600604
Simon Glass761648b2022-01-29 14:14:11 -0700605 gitutil.setup()
Simon Glass9f1ba0f2016-07-27 20:33:02 -0600606 col = terminal.Color()
Simon Glassc05694f2013-04-03 11:07:16 +0000607
Simon Glassa56ac992023-07-19 17:49:04 -0600608 git_dir = os.path.join(args.git, '.git')
Simon Glassc05694f2013-04-03 11:07:16 +0000609
Simon Glassa56ac992023-07-19 17:49:04 -0600610 toolchains = get_toolchains(toolchains, col, args.override_toolchain,
611 args.fetch_arch, args.list_tool_chains,
612 args.verbose)
Simon Glass18a07252023-08-03 12:51:36 -0600613 if isinstance(toolchains, int):
614 return toolchains
615
Simon Glass3b300c52023-07-19 17:48:49 -0600616 output_dir = setup_output_dir(
Simon Glassa56ac992023-07-19 17:49:04 -0600617 args.output_dir, args.work_in_output, args.branch,
618 args.no_subdirs, col, clean_dir)
Simon Glass6029af12020-04-09 15:08:51 -0600619
Simon Glassc05694f2013-04-03 11:07:16 +0000620 # Work out what subset of the boards we are building
Simon Glass5df45222022-07-11 19:04:00 -0600621 if not brds:
Simon Glassa56ac992023-07-19 17:49:04 -0600622 brds = get_boards_obj(output_dir, args.regen_board_list,
Simon Glass66d4c882023-07-19 17:49:30 -0600623 args.maintainer_check, args.full_check,
Simon Glass89592fc2023-09-07 10:00:18 -0600624 args.threads, args.verbose and
625 not args.print_arch and not args.print_prefix)
Simon Glassabcc11d2023-07-19 17:48:41 -0600626 if isinstance(brds, int):
627 return brds
Simon Glass924c73a2014-08-28 09:43:41 -0600628
Simon Glass72f8bff2023-07-19 17:48:39 -0600629 selected, why_selected, board_warnings = determine_boards(
Simon Glassa56ac992023-07-19 17:49:04 -0600630 brds, args.terms, col, args.boards, args.exclude)
Simon Glassc05694f2013-04-03 11:07:16 +0000631
Simon Glassa56ac992023-07-19 17:49:04 -0600632 if args.print_prefix:
Simon Glasseaf1be22023-07-19 17:48:56 -0600633 show_toolchain_prefix(brds, toolchains)
Simon Glass48ac42e2019-12-05 15:59:14 -0700634 return 0
635
Simon Glassa8a0ce72023-07-19 17:49:28 -0600636 if args.print_arch:
637 show_arch(brds)
638 return 0
639
Simon Glassa56ac992023-07-19 17:49:04 -0600640 series = determine_series(selected, col, git_dir, args.count,
641 args.branch, args.work_in_output)
Simon Glassc05694f2013-04-03 11:07:16 +0000642
Simon Glassa56ac992023-07-19 17:49:04 -0600643 adjust_args(args, series, selected)
Simon Glassc05694f2013-04-03 11:07:16 +0000644
Simon Glass70274872023-07-19 17:48:47 -0600645 # For a dry run, just show our actions as a sanity check
Simon Glassa56ac992023-07-19 17:49:04 -0600646 if args.dry_run:
Simon Glass70274872023-07-19 17:48:47 -0600647 show_actions(series, why_selected, selected, output_dir, board_warnings,
Simon Glassa56ac992023-07-19 17:49:04 -0600648 args.step, args.threads, args.jobs,
649 args.verbose)
Simon Glass70274872023-07-19 17:48:47 -0600650 return 0
Simon Glassc05694f2013-04-03 11:07:16 +0000651
Simon Glassa56ac992023-07-19 17:49:04 -0600652 # Create a new builder with the selected args
Simon Glass2183b842023-07-19 17:48:33 -0600653 builder = Builder(toolchains, output_dir, git_dir,
Simon Glassa56ac992023-07-19 17:49:04 -0600654 args.threads, args.jobs, checkout=True,
655 show_unknown=args.show_unknown, step=args.step,
656 no_subdirs=args.no_subdirs, full_path=args.full_path,
657 verbose_build=args.verbose_build,
658 mrproper=args.mrproper,
659 per_board_out_dir=args.per_board_out_dir,
660 config_only=args.config_only,
661 squash_config_y=not args.preserve_config_y,
662 warnings_as_errors=args.warnings_as_errors,
663 work_in_output=args.work_in_output,
Simon Glasse5650a82022-01-22 05:07:33 -0700664 test_thread_exceptions=test_thread_exceptions,
Simon Glassa56ac992023-07-19 17:49:04 -0600665 adjust_cfg=calc_adjust_cfg(args.adjust_cfg,
666 args.reproducible_builds),
667 allow_missing=get_allow_missing(args.allow_missing,
668 args.no_allow_missing,
669 len(selected), args.branch),
670 no_lto=args.no_lto,
671 reproducible_builds=args.reproducible_builds,
672 force_build = args.force_build,
673 force_build_failures = args.force_build_failures,
674 force_reconfig = args.force_reconfig, in_tree = args.in_tree,
675 force_config_on_failure=not args.quick, make_func=make_func)
Simon Glassc05694f2013-04-03 11:07:16 +0000676
Simon Glasscf91d312023-07-19 17:48:52 -0600677 TEST_BUILDER = builder
Simon Glassc05694f2013-04-03 11:07:16 +0000678
Simon Glass00401d92023-07-19 17:48:55 -0600679 return run_builder(builder, series.commits if series else None,
Simon Glassa56ac992023-07-19 17:49:04 -0600680 brds.get_selected_dict(), args)