diff options
Diffstat (limited to 'tools/buildman/control.py')
-rw-r--r-- | tools/buildman/control.py | 790 |
1 files changed, 525 insertions, 265 deletions
diff --git a/tools/buildman/control.py b/tools/buildman/control.py index 09a11f2..5c57200 100644 --- a/tools/buildman/control.py +++ b/tools/buildman/control.py @@ -2,15 +2,14 @@ # Copyright (c) 2013 The Chromium OS Authors. # +"""Control module for buildman + +This holds the main control logic for buildman, when not running tests. +""" + import multiprocessing -try: - import importlib.resources -except ImportError: - # for Python 3.6 - import importlib_resources import os import shutil -import subprocess import sys from buildman import boards @@ -22,34 +21,58 @@ from patman import gitutil from patman import patchstream from u_boot_pylib import command from u_boot_pylib import terminal -from u_boot_pylib import tools from u_boot_pylib.terminal import tprint -def GetPlural(count): +TEST_BUILDER = None + +def get_plural(count): """Returns a plural 's' if count is not 1""" return 's' if count != 1 else '' -def GetActionSummary(is_summary, commits, selected, options): - """Return a string summarising the intended action. + +def count_build_commits(commits, step): + """Calculate the number of commits to be built + + Args: + commits (list of Commit): Commits to build or None + step (int): Step value for commits, typically 1 Returns: - Summary string. + Number of commits that will be built """ if commits: count = len(commits) - count = (count + options.step - 1) // options.step - commit_str = '%d commit%s' % (count, GetPlural(count)) + return (count + step - 1) // step + return 0 + + +def get_action_summary(is_summary, commit_count, selected, threads, jobs): + """Return a string summarising the intended action. + + Args: + is_summary (bool): True if this is a summary (otherwise it is building) + commits (list): List of commits being built + selected (list of Board): List of Board objects that are marked + step (int): Step increment through commits + threads (int): Number of processor threads being used + jobs (int): Number of jobs to build at once + + Returns: + Summary string. + """ + if commit_count: + commit_str = f'{commit_count} commit{get_plural(commit_count)}' else: commit_str = 'current source' - str = '%s %s for %d boards' % ( - 'Summary of' if is_summary else 'Building', commit_str, - len(selected)) - str += ' (%d thread%s, %d job%s per thread)' % (options.threads, - GetPlural(options.threads), options.jobs, GetPlural(options.jobs)) - return str - -def ShowActions(series, why_selected, boards_selected, builder, options, - board_warnings): + msg = (f"{'Summary of' if is_summary else 'Building'} " + f'{commit_str} for {len(selected)} boards') + msg += (f' ({threads} thread{get_plural(threads)}, ' + f'{jobs} job{get_plural(jobs)} per thread)') + return msg + +# pylint: disable=R0913 +def show_actions(series, why_selected, boards_selected, output_dir, + board_warnings, step, threads, jobs, verbose): """Display a list of actions that we would take, if not a dry run. Args: @@ -61,9 +84,12 @@ def ShowActions(series, why_selected, boards_selected, builder, options, the value would be a list of board names. boards_selected: Dict of selected boards, key is target name, value is Board object - builder: The builder that will be used to build the commits - options: Command line options object + output_dir (str): Output directory for builder board_warnings: List of warnings obtained from board selected + step (int): Step increment through commits + threads (int): Number of processor threads being used + jobs (int): Number of jobs to build at once + verbose (bool): True to indicate why each board was selected """ col = terminal.Color() print('Dry run, so not doing much. But I would do this:') @@ -72,27 +98,27 @@ def ShowActions(series, why_selected, boards_selected, builder, options, commits = series.commits else: commits = None - print(GetActionSummary(False, commits, boards_selected, - options)) - print('Build directory: %s' % builder.base_dir) + print(get_action_summary(False, count_build_commits(commits, step), + boards_selected, threads, jobs)) + print(f'Build directory: {output_dir}') if commits: - for upto in range(0, len(series.commits), options.step): + for upto in range(0, len(series.commits), step): commit = series.commits[upto] print(' ', col.build(col.YELLOW, commit.hash[:8], bright=False), end=' ') print(commit.subject) print() for arg in why_selected: if arg != 'all': - print(arg, ': %d boards' % len(why_selected[arg])) - if options.verbose: - print(' %s' % ' '.join(why_selected[arg])) - print(('Total boards to build for each commit: %d\n' % - len(why_selected['all']))) + print(arg, f': {len(why_selected[arg])} boards') + if verbose: + print(f" {' '.join(why_selected[arg])}") + print('Total boards to build for each ' + f"commit: {len(why_selected['all'])}\n") if board_warnings: for warning in board_warnings: print(col.build(col.YELLOW, warning)) -def ShowToolchainPrefix(brds, toolchains): +def show_toolchain_prefix(brds, toolchains): """Show information about a the tool chain used by one or more boards The function checks that all boards use the same toolchain, then prints @@ -110,15 +136,48 @@ def ShowToolchainPrefix(brds, toolchains): for brd in board_selected.values(): tc_set.add(toolchains.Select(brd.arch)) if len(tc_set) != 1: - return 'Supplied boards must share one toolchain' - return False - tc = tc_set.pop() - print(tc.GetEnvArgs(toolchain.VAR_CROSS_COMPILE)) - return None + sys.exit('Supplied boards must share one toolchain') + tchain = tc_set.pop() + print(tchain.GetEnvArgs(toolchain.VAR_CROSS_COMPILE)) + +def show_arch(brds): + """Show information about a the architecture used by one or more boards + + The function checks that all boards use the same architecture, then prints + the correct value for ARCH. + + Args: + boards: Boards object containing selected boards + + Return: + None on success, string error message otherwise + """ + board_selected = brds.get_selected_dict() + arch_set = set() + for brd in board_selected.values(): + arch_set.add(brd.arch) + if len(arch_set) != 1: + sys.exit('Supplied boards must share one arch') + print(arch_set.pop()) def get_allow_missing(opt_allow, opt_no_allow, num_selected, has_branch): + """Figure out whether to allow external blobs + + Uses the allow-missing setting and the provided arguments to decide whether + missing external blobs should be allowed + + Args: + opt_allow (bool): True if --allow-missing flag is set + opt_no_allow (bool): True if --no-allow-missing flag is set + num_selected (int): Number of selected board + has_branch (bool): True if a git branch (to build) has been provided + + Returns: + bool: True to allow missing external blobs, False to produce an error if + external blobs are used + """ allow_missing = False - am_setting = bsettings.GetGlobalItemValue('allow-missing') + am_setting = bsettings.get_global_item_value('allow-missing') if am_setting: if am_setting == 'always': allow_missing = True @@ -133,142 +192,82 @@ def get_allow_missing(opt_allow, opt_no_allow, num_selected, has_branch): allow_missing = False return allow_missing -def DoBuildman(options, args, toolchains=None, make_func=None, brds=None, - clean_dir=False, test_thread_exceptions=False): - """The main control code for buildman - - Args: - options: Command line options object - args: Command line arguments (list of strings) - toolchains: Toolchains to use - this should be a Toolchains() - object. If None, then it will be created and scanned - make_func: Make function to use for the builder. This is called - to execute 'make'. If this is None, the normal function - will be used, which calls the 'make' tool with suitable - arguments. This setting is useful for tests. - brds: Boards() object to use, containing a list of available - boards. If this is None it will be created and scanned. - clean_dir: Used for tests only, indicates that the existing output_dir - should be removed before starting the build - test_thread_exceptions: Uses for tests only, True to make the threads - raise an exception instead of reporting their result. This simulates - a failure in the code somewhere - """ - global builder - - if options.full_help: - with importlib.resources.path('buildman', 'README.rst') as readme: - tools.print_full_help(str(readme)) - return 0 - gitutil.setup() - col = terminal.Color() +def count_commits(branch, count, col, git_dir): + """Could the number of commits in the branch/ranch being built - options.git_dir = os.path.join(options.git, '.git') - - no_toolchains = toolchains is None - if no_toolchains: - toolchains = toolchain.Toolchains(options.override_toolchain) - - if options.fetch_arch: - if options.fetch_arch == 'list': - sorted_list = toolchains.ListArchs() - print(col.build(col.BLUE, 'Available architectures: %s\n' % - ' '.join(sorted_list))) - return 0 - else: - fetch_arch = options.fetch_arch - if fetch_arch == 'all': - fetch_arch = ','.join(toolchains.ListArchs()) - print(col.build(col.CYAN, '\nDownloading toolchains: %s' % - fetch_arch)) - for arch in fetch_arch.split(','): - print() - ret = toolchains.FetchAndInstall(arch) - if ret: - return ret - return 0 - - if no_toolchains: - toolchains.GetSettings() - toolchains.Scan(options.list_tool_chains and options.verbose) - if options.list_tool_chains: - toolchains.List() - print() - return 0 - - if not options.output_dir: - if options.work_in_output: - sys.exit(col.build(col.RED, '-w requires that you specify -o')) - options.output_dir = '..' - - # Work out what subset of the boards we are building - if not brds: - if not os.path.exists(options.output_dir): - os.makedirs(options.output_dir) - board_file = os.path.join(options.output_dir, 'boards.cfg') - - brds = boards.Boards() - ok = brds.ensure_board_list(board_file, - options.threads or multiprocessing.cpu_count(), - force=options.regen_board_list, - quiet=not options.verbose) - if options.regen_board_list: - return 0 if ok else 2 - brds.read_boards(board_file) - - exclude = [] - if options.exclude: - for arg in options.exclude: - exclude += arg.split(',') - - if options.boards: - requested_boards = [] - for b in options.boards: - requested_boards += b.split(',') - else: - requested_boards = None - why_selected, board_warnings = brds.select_boards(args, exclude, - requested_boards) - selected = brds.get_selected() - if not len(selected): - sys.exit(col.build(col.RED, 'No matching boards found')) - - if options.print_prefix: - err = ShowToolchainPrefix(brds, toolchains) - if err: - sys.exit(col.build(col.RED, err)) - return 0 + Args: + branch (str): Name of branch to build, or None if none + count (int): Number of commits to build, or -1 for all + col (Terminal.Color): Color object to use + git_dir (str): Git directory to use, e.g. './.git' - # Work out how many commits to build. We want to build everything on the - # branch. We also build the upstream commit as a control so we can see - # problems introduced by the first commit on the branch. - count = options.count - has_range = options.branch and '..' in options.branch + Returns: + tuple: + Number of commits being built + True if the 'branch' string contains a range rather than a simple + name + """ + has_range = branch and '..' in branch if count == -1: - if not options.branch: + if not branch: count = 1 else: if has_range: - count, msg = gitutil.count_commits_in_range(options.git_dir, - options.branch) + count, msg = gitutil.count_commits_in_range(git_dir, branch) else: - count, msg = gitutil.count_commits_in_branch(options.git_dir, - options.branch) + count, msg = gitutil.count_commits_in_branch(git_dir, branch) if count is None: sys.exit(col.build(col.RED, msg)) elif count == 0: - sys.exit(col.build(col.RED, "Range '%s' has no commits" % - options.branch)) + sys.exit(col.build(col.RED, + f"Range '{branch}' has no commits")) if msg: print(col.build(col.YELLOW, msg)) count += 1 # Build upstream commit also if not count: - msg = ("No commits found to process in branch '%s': " - "set branch's upstream or use -c flag" % options.branch) + msg = (f"No commits found to process in branch '{branch}': " + "set branch's upstream or use -c flag") sys.exit(col.build(col.RED, msg)) - if options.work_in_output: + return count, has_range + + +def determine_series(selected, col, git_dir, count, branch, work_in_output): + """Determine the series which is to be built, if any + + If there is a series, the commits in that series are numbered by setting + their sequence value (starting from 0). This is used by tests. + + Args: + selected (list of Board): List of Board objects that are marked + selected + col (Terminal.Color): Color object to use + git_dir (str): Git directory to use, e.g. './.git' + count (int): Number of commits in branch + branch (str): Name of branch to build, or None if none + work_in_output (bool): True to work in the output directory + + Returns: + Series: Series to build, or None for none + + Read the metadata from the commits. First look at the upstream commit, + then the ones in the branch. We would like to do something like + upstream/master~..branch but that isn't possible if upstream/master is + a merge commit (it will list all the commits that form part of the + merge) + + Conflicting tags are not a problem for buildman, since it does not use + them. For example, Series-version is not useful for buildman. On the + other hand conflicting tags will cause an error. So allow later tags + to overwrite earlier ones by setting allow_overwrite=True + """ + + # Work out how many commits to build. We want to build everything on the + # branch. We also build the upstream commit as a control so we can see + # problems introduced by the first commit on the branch. + count, has_range = count_commits(branch, count, col, git_dir) + if work_in_output: if len(selected) != 1: sys.exit(col.build(col.RED, '-w can only be used with a single board')) @@ -276,141 +275,402 @@ def DoBuildman(options, args, toolchains=None, make_func=None, brds=None, sys.exit(col.build(col.RED, '-w can only be used with a single commit')) - # Read the metadata from the commits. First look at the upstream commit, - # then the ones in the branch. We would like to do something like - # upstream/master~..branch but that isn't possible if upstream/master is - # a merge commit (it will list all the commits that form part of the - # merge) - # Conflicting tags are not a problem for buildman, since it does not use - # them. For example, Series-version is not useful for buildman. On the - # other hand conflicting tags will cause an error. So allow later tags - # to overwrite earlier ones by setting allow_overwrite=True - if options.branch: + if branch: if count == -1: if has_range: - range_expr = options.branch + range_expr = branch else: - range_expr = gitutil.get_range_in_branch(options.git_dir, - options.branch) - upstream_commit = gitutil.get_upstream(options.git_dir, - options.branch) + range_expr = gitutil.get_range_in_branch(git_dir, branch) + upstream_commit = gitutil.get_upstream(git_dir, branch) series = patchstream.get_metadata_for_list(upstream_commit, - options.git_dir, 1, series=None, allow_overwrite=True) + git_dir, 1, series=None, allow_overwrite=True) series = patchstream.get_metadata_for_list(range_expr, - options.git_dir, None, series, allow_overwrite=True) + git_dir, None, series, allow_overwrite=True) else: # Honour the count - series = patchstream.get_metadata_for_list(options.branch, - options.git_dir, count, series=None, allow_overwrite=True) + series = patchstream.get_metadata_for_list(branch, + git_dir, count, series=None, allow_overwrite=True) + + # Number the commits for test purposes + for i, commit in enumerate(series.commits): + commit.sequence = i else: series = None - if not options.dry_run: - options.verbose = True - if not options.summary: - options.show_errors = True + return series + + +def do_fetch_arch(toolchains, col, fetch_arch): + """Handle the --fetch-arch option + + Args: + toolchains (Toolchains): Tool chains to use + col (terminal.Color): Color object to build + fetch_arch (str): Argument passed to the --fetch-arch option + + Returns: + int: Return code for buildman + """ + if fetch_arch == 'list': + sorted_list = toolchains.ListArchs() + print(col.build( + col.BLUE, + f"Available architectures: {' '.join(sorted_list)}\n")) + return 0 + + if fetch_arch == 'all': + fetch_arch = ','.join(toolchains.ListArchs()) + print(col.build(col.CYAN, + f'\nDownloading toolchains: {fetch_arch}')) + for arch in fetch_arch.split(','): + print() + ret = toolchains.FetchAndInstall(arch) + if ret: + return ret + return 0 + + +def get_toolchains(toolchains, col, override_toolchain, fetch_arch, + list_tool_chains, verbose): + """Get toolchains object to use + + Args: + toolchains (Toolchains or None): Toolchains to use. If None, then a + Toolchains object will be created and scanned + col (Terminal.Color): Color object + override_toolchain (str or None): Override value for toolchain, or None + fetch_arch (bool): True to fetch the toolchain for the architectures + list_tool_chains (bool): True to list all tool chains + verbose (bool): True for verbose output when listing toolchains + + Returns: + Either: + int: Operation completed and buildman should exit with exit code + Toolchains: Toolchains object to use + """ + no_toolchains = toolchains is None + if no_toolchains: + toolchains = toolchain.Toolchains(override_toolchain) + + if fetch_arch: + return do_fetch_arch(toolchains, col, fetch_arch) + + if no_toolchains: + toolchains.GetSettings() + toolchains.Scan(list_tool_chains and verbose) + if list_tool_chains: + toolchains.List() + print() + return 0 + return toolchains + + +def get_boards_obj(output_dir, regen_board_list, maintainer_check, full_check, + threads, verbose): + """Object the Boards object to use + + Creates the output directory and ensures there is a boards.cfg file, then + read it in. + + Args: + output_dir (str): Output directory to use + regen_board_list (bool): True to just regenerate the board list + maintainer_check (bool): True to just run a maintainer check + full_check (bool): True to just run a full check of Kconfig and + maintainers + threads (int or None): Number of threads to use to create boards file + verbose (bool): False to suppress output from boards-file generation + + Returns: + Either: + int: Operation completed and buildman should exit with exit code + Boards: Boards object to use + """ + brds = boards.Boards() + nr_cpus = threads or multiprocessing.cpu_count() + if maintainer_check or full_check: + warnings = brds.build_board_list(jobs=nr_cpus, + warn_targets=full_check)[1] + if warnings: + for warn in warnings: + print(warn, file=sys.stderr) + return 2 + return 0 + + if not os.path.exists(output_dir): + os.makedirs(output_dir) + board_file = os.path.join(output_dir, 'boards.cfg') + if regen_board_list and regen_board_list != '-': + board_file = regen_board_list + + okay = brds.ensure_board_list(board_file, nr_cpus, force=regen_board_list, + quiet=not verbose) + if regen_board_list: + return 0 if okay else 2 + brds.read_boards(board_file) + return brds + + +def determine_boards(brds, args, col, opt_boards, exclude_list): + """Determine which boards to build + + Each element of args and exclude can refer to a board name, arch or SoC + + Args: + brds (Boards): Boards object + args (list of str): Arguments describing boards to build + col (Terminal.Color): Color object + opt_boards (list of str): Specific boards to build, or None for all + exclude_list (list of str): Arguments describing boards to exclude + + Returns: + tuple: + list of Board: List of Board objects that are marked selected + why_selected: Dictionary where each key is a buildman argument + provided by the user, and the value is the list of boards + brought in by that argument. For example, 'arm' might bring + in 400 boards, so in this case the key would be 'arm' and + the value would be a list of board names. + board_warnings: List of warnings obtained from board selected + """ + exclude = [] + if exclude_list: + for arg in exclude_list: + exclude += arg.split(',') + + if opt_boards: + requested_boards = [] + for brd in opt_boards: + requested_boards += brd.split(',') + else: + requested_boards = None + why_selected, board_warnings = brds.select_boards(args, exclude, + requested_boards) + selected = brds.get_selected() + if not selected: + sys.exit(col.build(col.RED, 'No matching boards found')) + return selected, why_selected, board_warnings + + +def adjust_args(args, series, selected): + """Adjust arguments according to various constraints + + Updates verbose, show_errors, threads, jobs and step + + Args: + args (Namespace): Namespace object to adjust + series (Series): Series being built / summarised + selected (list of Board): List of Board objects that are marked + """ + if not series and not args.dry_run: + args.verbose = True + if not args.summary: + args.show_errors = True # By default we have one thread per CPU. But if there are not enough jobs # we can have fewer threads and use a high '-j' value for make. - if options.threads is None: - options.threads = min(multiprocessing.cpu_count(), len(selected)) - if not options.jobs: - options.jobs = max(1, (multiprocessing.cpu_count() + + if args.threads is None: + args.threads = min(multiprocessing.cpu_count(), len(selected)) + if not args.jobs: + args.jobs = max(1, (multiprocessing.cpu_count() + len(selected) - 1) // len(selected)) - if not options.step: - options.step = len(series.commits) - 1 + if not args.step: + args.step = len(series.commits) - 1 + + # We can't show function sizes without board details at present + if args.show_bloat: + args.show_detail = True - gnu_make = command.output(os.path.join(options.git, - 'scripts/show-gnu-make'), raise_on_error=False).rstrip() - if not gnu_make: - sys.exit('GNU Make not found') - allow_missing = get_allow_missing(options.allow_missing, - options.no_allow_missing, len(selected), - options.branch) +def setup_output_dir(output_dir, work_in_output, branch, no_subdirs, col, + clean_dir): + """Set up the output directory - # Create a new builder with the selected options. - output_dir = options.output_dir - if options.branch: - dirname = options.branch.replace('/', '_') + Args: + output_dir (str): Output directory provided by the user, or None if none + work_in_output (bool): True to work in the output directory + branch (str): Name of branch to build, or None if none + no_subdirs (bool): True to put the output in the top-level output dir + clean_dir: Used for tests only, indicates that the existing output_dir + should be removed before starting the build + + Returns: + str: Updated output directory pathname + """ + if not output_dir: + if work_in_output: + sys.exit(col.build(col.RED, '-w requires that you specify -o')) + output_dir = '..' + if branch and not no_subdirs: # As a special case allow the board directory to be placed in the # output directory itself rather than any subdirectory. - if not options.no_subdirs: - output_dir = os.path.join(options.output_dir, dirname) + dirname = branch.replace('/', '_') + output_dir = os.path.join(output_dir, dirname) if clean_dir and os.path.exists(output_dir): shutil.rmtree(output_dir) - adjust_cfg = cfgutil.convert_list_to_dict(options.adjust_cfg) + return output_dir + + +def run_builder(builder, commits, board_selected, args): + """Run the builder or show the summary + + Args: + commits (list of Commit): List of commits being built, None if no branch + boards_selected (dict): Dict of selected boards: + key: target name + value: Board object + args (Namespace): Namespace to use + + Returns: + int: Return code for buildman + """ + gnu_make = command.output(os.path.join(args.git, + 'scripts/show-gnu-make'), raise_on_error=False).rstrip() + if not gnu_make: + sys.exit('GNU Make not found') + builder.gnu_make = gnu_make + + if not args.ide: + commit_count = count_build_commits(commits, args.step) + tprint(get_action_summary(args.summary, commit_count, board_selected, + args.threads, args.jobs)) + + builder.set_display_options( + args.show_errors, args.show_sizes, args.show_detail, args.show_bloat, + args.list_error_boards, args.show_config, args.show_environment, + args.filter_dtb_warnings, args.filter_migration_warnings, args.ide) + if args.summary: + builder.show_summary(commits, board_selected) + else: + fail, warned, excs = builder.build_boards( + commits, board_selected, args.keep_outputs, args.verbose) + if excs: + return 102 + if fail: + return 100 + if warned and not args.ignore_warnings: + return 101 + return 0 + + +def calc_adjust_cfg(adjust_cfg, reproducible_builds): + """Calculate the value to use for adjust_cfg + + Args: + adjust_cfg (list of str): List of configuration changes. See cfgutil for + details + reproducible_builds (bool): True to adjust the configuration to get + reproduceable builds + + Returns: + adjust_cfg (list of str): List of configuration changes + """ + adjust_cfg = cfgutil.convert_list_to_dict(adjust_cfg) # Drop LOCALVERSION_AUTO since it changes the version string on every commit - if options.reproducible_builds: + if reproducible_builds: # If these are mentioned, leave the local version alone if 'LOCALVERSION' in adjust_cfg or 'LOCALVERSION_AUTO' in adjust_cfg: print('Not dropping LOCALVERSION_AUTO for reproducible build') else: adjust_cfg['LOCALVERSION_AUTO'] = '~' + return adjust_cfg - builder = Builder(toolchains, output_dir, options.git_dir, - options.threads, options.jobs, gnu_make=gnu_make, checkout=True, - show_unknown=options.show_unknown, step=options.step, - no_subdirs=options.no_subdirs, full_path=options.full_path, - verbose_build=options.verbose_build, - mrproper=options.mrproper, - per_board_out_dir=options.per_board_out_dir, - config_only=options.config_only, - squash_config_y=not options.preserve_config_y, - warnings_as_errors=options.warnings_as_errors, - work_in_output=options.work_in_output, - test_thread_exceptions=test_thread_exceptions, - adjust_cfg=adjust_cfg, - allow_missing=allow_missing, no_lto=options.no_lto, - reproducible_builds=options.reproducible_builds) - builder.force_config_on_failure = not options.quick - if make_func: - builder.do_make = make_func + +def do_buildman(args, toolchains=None, make_func=None, brds=None, + clean_dir=False, test_thread_exceptions=False): + """The main control code for buildman + + Args: + args: ArgumentParser object + args: Command line arguments (list of strings) + toolchains: Toolchains to use - this should be a Toolchains() + object. If None, then it will be created and scanned + make_func: Make function to use for the builder. This is called + to execute 'make'. If this is None, the normal function + will be used, which calls the 'make' tool with suitable + arguments. This setting is useful for tests. + brds: Boards() object to use, containing a list of available + boards. If this is None it will be created and scanned. + clean_dir: Used for tests only, indicates that the existing output_dir + should be removed before starting the build + test_thread_exceptions: Uses for tests only, True to make the threads + raise an exception instead of reporting their result. This simulates + a failure in the code somewhere + """ + # Used so testing can obtain the builder: pylint: disable=W0603 + global TEST_BUILDER + + gitutil.setup() + col = terminal.Color() + + git_dir = os.path.join(args.git, '.git') + + toolchains = get_toolchains(toolchains, col, args.override_toolchain, + args.fetch_arch, args.list_tool_chains, + args.verbose) + output_dir = setup_output_dir( + args.output_dir, args.work_in_output, args.branch, + args.no_subdirs, col, clean_dir) + + # Work out what subset of the boards we are building + if not brds: + brds = get_boards_obj(output_dir, args.regen_board_list, + args.maintainer_check, args.full_check, + args.threads, args.verbose) + if isinstance(brds, int): + return brds + + selected, why_selected, board_warnings = determine_boards( + brds, args.terms, col, args.boards, args.exclude) + + if args.print_prefix: + show_toolchain_prefix(brds, toolchains) + return 0 + + if args.print_arch: + show_arch(brds) + return 0 + + series = determine_series(selected, col, git_dir, args.count, + args.branch, args.work_in_output) + + adjust_args(args, series, selected) # For a dry run, just show our actions as a sanity check - if options.dry_run: - ShowActions(series, why_selected, selected, builder, options, - board_warnings) - else: - builder.force_build = options.force_build - builder.force_build_failures = options.force_build_failures - builder.force_reconfig = options.force_reconfig - builder.in_tree = options.in_tree - - # Work out which boards to build - board_selected = brds.get_selected_dict() - - if series: - commits = series.commits - # Number the commits for test purposes - for commit in range(len(commits)): - commits[commit].sequence = commit - else: - commits = None - - if not options.ide: - tprint(GetActionSummary(options.summary, commits, board_selected, - options)) - - # We can't show function sizes without board details at present - if options.show_bloat: - options.show_detail = True - builder.SetDisplayOptions( - options.show_errors, options.show_sizes, options.show_detail, - options.show_bloat, options.list_error_boards, options.show_config, - options.show_environment, options.filter_dtb_warnings, - options.filter_migration_warnings, options.ide) - if options.summary: - builder.ShowSummary(commits, board_selected) - else: - fail, warned, excs = builder.BuildBoards( - commits, board_selected, options.keep_outputs, options.verbose) - if excs: - return 102 - elif fail: - return 100 - elif warned and not options.ignore_warnings: - return 101 - return 0 + if args.dry_run: + show_actions(series, why_selected, selected, output_dir, board_warnings, + args.step, args.threads, args.jobs, + args.verbose) + return 0 + + # Create a new builder with the selected args + builder = Builder(toolchains, output_dir, git_dir, + args.threads, args.jobs, checkout=True, + show_unknown=args.show_unknown, step=args.step, + no_subdirs=args.no_subdirs, full_path=args.full_path, + verbose_build=args.verbose_build, + mrproper=args.mrproper, + per_board_out_dir=args.per_board_out_dir, + config_only=args.config_only, + squash_config_y=not args.preserve_config_y, + warnings_as_errors=args.warnings_as_errors, + work_in_output=args.work_in_output, + test_thread_exceptions=test_thread_exceptions, + adjust_cfg=calc_adjust_cfg(args.adjust_cfg, + args.reproducible_builds), + allow_missing=get_allow_missing(args.allow_missing, + args.no_allow_missing, + len(selected), args.branch), + no_lto=args.no_lto, + reproducible_builds=args.reproducible_builds, + force_build = args.force_build, + force_build_failures = args.force_build_failures, + force_reconfig = args.force_reconfig, in_tree = args.in_tree, + force_config_on_failure=not args.quick, make_func=make_func) + + TEST_BUILDER = builder + + return run_builder(builder, series.commits if series else None, + brds.get_selected_dict(), args) |