]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | # SPDX-License-Identifier: GPL-2.0+ |
fc3fe1c2 SG |
2 | # Copyright (c) 2013 The Chromium OS Authors. |
3 | # | |
fc3fe1c2 | 4 | |
9ef05b95 SG |
5 | """Control module for buildman |
6 | ||
7 | This holds the main control logic for buildman, when not running tests. | |
8 | """ | |
9 | ||
fc3fe1c2 SG |
10 | import multiprocessing |
11 | import os | |
883a321a | 12 | import shutil |
fc3fe1c2 SG |
13 | import sys |
14 | ||
c52bd225 | 15 | from buildman import boards |
0ede00fd | 16 | from buildman import bsettings |
2b4806e4 | 17 | from buildman import cfgutil |
0ede00fd SG |
18 | from buildman import toolchain |
19 | from buildman.builder import Builder | |
bf776679 SG |
20 | from patman import gitutil |
21 | from patman import patchstream | |
4583c002 SG |
22 | from u_boot_pylib import command |
23 | from u_boot_pylib import terminal | |
4583c002 | 24 | from u_boot_pylib.terminal import tprint |
fc3fe1c2 | 25 | |
b8be2bd8 SG |
26 | TEST_BUILDER = None |
27 | ||
9ef05b95 | 28 | def get_plural(count): |
fc3fe1c2 SG |
29 | """Returns a plural 's' if count is not 1""" |
30 | return 's' if count != 1 else '' | |
31 | ||
1d0c55d8 SG |
32 | |
33 | def 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 | ||
49 | def get_action_summary(is_summary, commit_count, selected, threads, jobs): | |
fc3fe1c2 SG |
50 | """Return a string summarising the intended action. |
51 | ||
1d3a5a52 SG |
52 | 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 | ||
fc3fe1c2 SG |
60 | Returns: |
61 | Summary string. | |
62 | """ | |
1d0c55d8 SG |
63 | if commit_count: |
64 | commit_str = f'{commit_count} commit{get_plural(commit_count)}' | |
fea5858e SG |
65 | else: |
66 | commit_str = 'current source' | |
b8be2bd8 SG |
67 | msg = (f"{'Summary of' if is_summary else 'Building'} " |
68 | f'{commit_str} for {len(selected)} boards') | |
1d3a5a52 SG |
69 | msg += (f' ({threads} thread{get_plural(threads)}, ' |
70 | f'{jobs} job{get_plural(jobs)} per thread)') | |
b8be2bd8 SG |
71 | return msg |
72 | ||
73 | # pylint: disable=R0913 | |
1b820ee1 SG |
74 | def show_actions(series, why_selected, boards_selected, output_dir, |
75 | board_warnings, step, threads, jobs, verbose): | |
fc3fe1c2 SG |
76 | """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 | |
8d7523c5 SG |
81 | 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 | |
fc3fe1c2 SG |
84 | 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 | |
d233dfb0 | 87 | output_dir (str): Output directory for builder |
0689036a | 88 | board_warnings: List of warnings obtained from board selected |
1b820ee1 SG |
89 | 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 | |
fc3fe1c2 SG |
93 | """ |
94 | col = terminal.Color() | |
c05aa036 SG |
95 | print('Dry run, so not doing much. But I would do this:') |
96 | print() | |
fea5858e SG |
97 | if series: |
98 | commits = series.commits | |
99 | else: | |
100 | commits = None | |
1d0c55d8 SG |
101 | print(get_action_summary(False, count_build_commits(commits, step), |
102 | boards_selected, threads, jobs)) | |
d233dfb0 | 103 | print(f'Build directory: {output_dir}') |
fea5858e | 104 | if commits: |
1b820ee1 | 105 | for upto in range(0, len(series.commits), step): |
fea5858e | 106 | commit = series.commits[upto] |
252ac589 | 107 | print(' ', col.build(col.YELLOW, commit.hash[:8], bright=False), end=' ') |
c05aa036 SG |
108 | print(commit.subject) |
109 | print() | |
fc3fe1c2 SG |
110 | for arg in why_selected: |
111 | if arg != 'all': | |
b8be2bd8 | 112 | print(arg, f': {len(why_selected[arg])} boards') |
1b820ee1 | 113 | if verbose: |
b8be2bd8 SG |
114 | print(f" {' '.join(why_selected[arg])}") |
115 | print('Total boards to build for each ' | |
116 | f"commit: {len(why_selected['all'])}\n") | |
0689036a SG |
117 | if board_warnings: |
118 | for warning in board_warnings: | |
252ac589 | 119 | print(col.build(col.YELLOW, warning)) |
fc3fe1c2 | 120 | |
9ef05b95 | 121 | def show_toolchain_prefix(brds, toolchains): |
57cb9d52 SG |
122 | """Show information about a the tool chain used by one or more boards |
123 | ||
4e9162d5 SG |
124 | The function checks that all boards use the same toolchain, then prints |
125 | the correct value for CROSS_COMPILE. | |
57cb9d52 SG |
126 | |
127 | Args: | |
128 | boards: Boards object containing selected boards | |
129 | toolchains: Toolchains object containing available toolchains | |
57cb9d52 SG |
130 | |
131 | Return: | |
132 | None on success, string error message otherwise | |
133 | """ | |
6014db68 | 134 | board_selected = brds.get_selected_dict() |
57cb9d52 | 135 | tc_set = set() |
cc2c0d18 | 136 | for brd in board_selected.values(): |
57cb9d52 SG |
137 | tc_set.add(toolchains.Select(brd.arch)) |
138 | if len(tc_set) != 1: | |
ea78233d | 139 | sys.exit('Supplied boards must share one toolchain') |
b8be2bd8 SG |
140 | tchain = tc_set.pop() |
141 | print(tchain.GetEnvArgs(toolchain.VAR_CROSS_COMPILE)) | |
57cb9d52 | 142 | |
d7713ad3 | 143 | def get_allow_missing(opt_allow, opt_no_allow, num_selected, has_branch): |
b8be2bd8 SG |
144 | """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 | """ | |
d7713ad3 TR |
159 | 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 | ||
d230c014 | 175 | |
aeb2381b SG |
176 | def 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 | ||
9df59e4c | 216 | def determine_series(selected, col, git_dir, count, branch, work_in_output): |
d230c014 SG |
217 | """Determine the series which is to be built, if any |
218 | ||
6378bad3 SG |
219 | 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 | ||
d230c014 | 222 | Args: |
1d3a5a52 | 223 | selected (list of Board): List of Board objects that are marked |
9df59e4c SG |
224 | selected |
225 | col (Terminal.Color): Color object to use | |
226 | git_dir (str): Git directory to use, e.g. './.git' | |
d230c014 | 227 | count (int): Number of commits in branch |
d230c014 | 228 | branch (str): Name of branch to build, or None if none |
9df59e4c | 229 | work_in_output (bool): True to work in the output directory |
d230c014 SG |
230 | |
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 | """ | |
9df59e4c SG |
245 | |
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. | |
aeb2381b | 249 | count, has_range = count_commits(branch, count, col, git_dir) |
9df59e4c SG |
250 | 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 | ||
d230c014 SG |
258 | 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) | |
6378bad3 SG |
274 | |
275 | # Number the commits for test purposes | |
276 | for i, commit in enumerate(series.commits): | |
277 | commit.sequence = i | |
d230c014 SG |
278 | else: |
279 | series = None | |
280 | return series | |
281 | ||
282 | ||
f7a36d54 SG |
283 | def 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 | ||
b8680646 SG |
313 | def 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 | ||
180c718a SG |
348 | def 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 | ||
0d4874fc SG |
391 | def 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 | ||
168d792f SG |
432 | def 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 | ||
4ec76822 SG |
458 | # We can't show function sizes without board details at present |
459 | if options.show_bloat: | |
460 | options.show_detail = True | |
461 | ||
e48b946b SG |
462 | |
463 | def 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 | ||
a659b8dc | 491 | |
68f917c0 SG |
492 | def 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 | """ | |
a659b8dc SG |
505 | 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 | ||
68f917c0 | 511 | if not options.ide: |
1d0c55d8 SG |
512 | commit_count = count_build_commits(commits, options.step) |
513 | tprint(get_action_summary(options.summary, commit_count, board_selected, | |
514 | options.threads, options.jobs)) | |
68f917c0 SG |
515 | |
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 | |
e48b946b | 533 | |
75584e1f SG |
534 | |
535 | def 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 | ||
9ef05b95 SG |
559 | def do_buildman(options, args, toolchains=None, make_func=None, brds=None, |
560 | clean_dir=False, test_thread_exceptions=False): | |
fc3fe1c2 SG |
561 | """The main control code for buildman |
562 | ||
563 | Args: | |
564 | options: Command line options object | |
565 | args: Command line arguments (list of strings) | |
d4144e45 SG |
566 | 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. | |
cc2c0d18 | 572 | brds: Boards() object to use, containing a list of available |
823e60b6 | 573 | boards. If this is None it will be created and scanned. |
24993313 SG |
574 | clean_dir: Used for tests only, indicates that the existing output_dir |
575 | should be removed before starting the build | |
8116c78f SG |
576 | 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 | |
fc3fe1c2 | 579 | """ |
b8be2bd8 SG |
580 | # Used so testing can obtain the builder: pylint: disable=W0603 |
581 | global TEST_BUILDER | |
883a321a | 582 | |
0157b187 | 583 | gitutil.setup() |
713bea38 | 584 | col = terminal.Color() |
fc3fe1c2 | 585 | |
d230c014 | 586 | git_dir = os.path.join(options.git, '.git') |
fc3fe1c2 | 587 | |
b8680646 SG |
588 | toolchains = get_toolchains(toolchains, col, options.override_toolchain, |
589 | options.fetch_arch, options.list_tool_chains, | |
590 | options.verbose) | |
e48b946b SG |
591 | output_dir = setup_output_dir( |
592 | options.output_dir, options.work_in_output, options.branch, | |
593 | options.no_subdirs, col, clean_dir) | |
eb70a2c0 | 594 | |
fc3fe1c2 | 595 | # Work out what subset of the boards we are building |
cc2c0d18 | 596 | if not brds: |
372b4458 | 597 | brds = get_boards_obj(output_dir, options.regen_board_list, |
180c718a SG |
598 | options.maintainer_check, options.threads, |
599 | options.verbose) | |
600 | if isinstance(brds, int): | |
601 | return brds | |
3cf4ae6f | 602 | |
0d4874fc SG |
603 | selected, why_selected, board_warnings = determine_boards( |
604 | brds, args, col, options.boards, options.exclude) | |
fc3fe1c2 | 605 | |
4e9162d5 | 606 | if options.print_prefix: |
ea78233d | 607 | show_toolchain_prefix(brds, toolchains) |
57cb9d52 SG |
608 | return 0 |
609 | ||
9df59e4c SG |
610 | series = determine_series(selected, col, git_dir, options.count, |
611 | options.branch, options.work_in_output) | |
fc3fe1c2 | 612 | |
168d792f | 613 | adjust_options(options, series, selected) |
fc3fe1c2 | 614 | |
168d792f SG |
615 | # 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 | |
fc3fe1c2 | 621 | |
ffd06d3d | 622 | # Create a new builder with the selected options |
d230c014 | 623 | builder = Builder(toolchains, output_dir, git_dir, |
a659b8dc | 624 | options.threads, options.jobs, checkout=True, |
5971ab5c | 625 | show_unknown=options.show_unknown, step=options.step, |
d2ce658d | 626 | no_subdirs=options.no_subdirs, full_path=options.full_path, |
f79f1e0c | 627 | verbose_build=options.verbose_build, |
eb70a2c0 | 628 | mrproper=options.mrproper, |
b50113f3 | 629 | per_board_out_dir=options.per_board_out_dir, |
b464f8e7 | 630 | config_only=options.config_only, |
2371d1bc | 631 | squash_config_y=not options.preserve_config_y, |
d829f121 | 632 | warnings_as_errors=options.warnings_as_errors, |
8116c78f | 633 | work_in_output=options.work_in_output, |
2b4806e4 | 634 | test_thread_exceptions=test_thread_exceptions, |
75584e1f SG |
635 | adjust_cfg=calc_adjust_cfg(options.adjust_cfg, |
636 | options.reproducible_builds), | |
f6df5edc SG |
637 | allow_missing=get_allow_missing(options.allow_missing, |
638 | options.no_allow_missing, | |
639 | len(selected), options.branch), | |
640 | no_lto=options.no_lto, | |
ffd06d3d SG |
641 | 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) | |
646 | ||
b8be2bd8 | 647 | TEST_BUILDER = builder |
f0207d77 | 648 | |
985d7ae4 SG |
649 | return run_builder(builder, series.commits if series else None, |
650 | brds.get_selected_dict(), options) |