aboutsummaryrefslogtreecommitdiff
path: root/tools/buildman/control.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/buildman/control.py')
-rw-r--r--tools/buildman/control.py778
1 files changed, 513 insertions, 265 deletions
diff --git a/tools/buildman/control.py b/tools/buildman/control.py
index 09a11f2..8a29534 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
+def count_commits(branch, count, col, git_dir):
+ """Could the number of commits in the branch/ranch being built
- 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()
-
- 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,390 @@ 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, 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
+ 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
+ """
+ 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
+
+ brds = boards.Boards()
+ okay = brds.ensure_board_list(
+ board_file,
+ threads or multiprocessing.cpu_count(),
+ 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):
+ """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 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:
+ for arg in exclude:
+ 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
+
+ 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
- # Create a new builder with the selected options.
- output_dir = options.output_dir
- if options.branch:
- dirname = options.branch.replace('/', '_')
+ 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.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)