diff options
Diffstat (limited to 'roms/u-boot/tools/buildman/control.py')
-rw-r--r-- | roms/u-boot/tools/buildman/control.py | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/roms/u-boot/tools/buildman/control.py b/roms/u-boot/tools/buildman/control.py new file mode 100644 index 000000000..a98d1b4c0 --- /dev/null +++ b/roms/u-boot/tools/buildman/control.py @@ -0,0 +1,385 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2013 The Chromium OS Authors. +# + +import multiprocessing +import os +import shutil +import subprocess +import sys + +from buildman import board +from buildman import bsettings +from buildman import toolchain +from buildman.builder import Builder +from patman import command +from patman import gitutil +from patman import patchstream +from patman import terminal +from patman.terminal import Print + +def GetPlural(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. + + Returns: + Summary string. + """ + if commits: + count = len(commits) + count = (count + options.step - 1) // options.step + commit_str = '%d commit%s' % (count, GetPlural(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): + """Display a list of actions that we would take, if not a dry run. + + Args: + series: Series object + 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. + 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 + board_warnings: List of warnings obtained from board selected + """ + col = terminal.Color() + print('Dry run, so not doing much. But I would do this:') + print() + if series: + commits = series.commits + else: + commits = None + print(GetActionSummary(False, commits, boards_selected, + options)) + print('Build directory: %s' % builder.base_dir) + if commits: + for upto in range(0, len(series.commits), options.step): + commit = series.commits[upto] + print(' ', col.Color(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']))) + if board_warnings: + for warning in board_warnings: + print(col.Color(col.YELLOW, warning)) + +def ShowToolchainPrefix(boards, 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 + the correct value for CROSS_COMPILE. + + Args: + boards: Boards object containing selected boards + toolchains: Toolchains object containing available toolchains + + Return: + None on success, string error message otherwise + """ + boards = boards.GetSelectedDict() + tc_set = set() + for brd in boards.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 + +def DoBuildman(options, args, toolchains=None, make_func=None, boards=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. + board: 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: + pager = os.getenv('PAGER') + if not pager: + pager = 'more' + fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])), + 'README') + command.Run(pager, fname) + 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.Color(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.Color(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 options.incremental: + print(col.Color(col.RED, + 'Warning: -I has been removed. See documentation')) + if not options.output_dir: + if options.work_in_output: + sys.exit(col.Color(col.RED, '-w requires that you specify -o')) + options.output_dir = '..' + + # Work out what subset of the boards we are building + if not boards: + if not os.path.exists(options.output_dir): + os.makedirs(options.output_dir) + board_file = os.path.join(options.output_dir, 'boards.cfg') + our_path = os.path.dirname(os.path.realpath(__file__)) + genboardscfg = os.path.join(our_path, '../genboardscfg.py') + if not os.path.exists(genboardscfg): + genboardscfg = os.path.join(options.git, 'tools/genboardscfg.py') + status = subprocess.call([genboardscfg, '-q', '-o', board_file]) + if status != 0: + # Older versions don't support -q + status = subprocess.call([genboardscfg, '-o', board_file]) + if status != 0: + sys.exit("Failed to generate boards.cfg") + + boards = board.Boards() + boards.ReadBoards(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 = boards.SelectBoards(args, exclude, + requested_boards) + selected = boards.GetSelected() + if not len(selected): + sys.exit(col.Color(col.RED, 'No matching boards found')) + + if options.print_prefix: + err = ShowToolchainPrefix(boards, toolchains) + if err: + sys.exit(col.Color(col.RED, err)) + return 0 + + # 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 + if count == -1: + if not options.branch: + count = 1 + else: + if has_range: + count, msg = gitutil.CountCommitsInRange(options.git_dir, + options.branch) + else: + count, msg = gitutil.CountCommitsInBranch(options.git_dir, + options.branch) + if count is None: + sys.exit(col.Color(col.RED, msg)) + elif count == 0: + sys.exit(col.Color(col.RED, "Range '%s' has no commits" % + options.branch)) + if msg: + print(col.Color(col.YELLOW, msg)) + count += 1 # Build upstream commit also + + if not count: + str = ("No commits found to process in branch '%s': " + "set branch's upstream or use -c flag" % options.branch) + sys.exit(col.Color(col.RED, str)) + if options.work_in_output: + if len(selected) != 1: + sys.exit(col.Color(col.RED, + '-w can only be used with a single board')) + if count != 1: + sys.exit(col.Color(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 count == -1: + if has_range: + range_expr = options.branch + else: + range_expr = gitutil.GetRangeInBranch(options.git_dir, + options.branch) + upstream_commit = gitutil.GetUpstream(options.git_dir, + options.branch) + series = patchstream.get_metadata_for_list(upstream_commit, + options.git_dir, 1, series=None, allow_overwrite=True) + + series = patchstream.get_metadata_for_list(range_expr, + options.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) + else: + series = None + if not options.dry_run: + options.verbose = True + if not options.summary: + options.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() + + len(selected) - 1) // len(selected)) + + if not options.step: + options.step = len(series.commits) - 1 + + 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') + + # Create a new builder with the selected options. + output_dir = options.output_dir + if options.branch: + dirname = options.branch.replace('/', '_') + # 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) + if clean_dir and os.path.exists(output_dir): + shutil.rmtree(output_dir) + 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) + builder.force_config_on_failure = not options.quick + if make_func: + builder.do_make = make_func + + # 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 = boards.GetSelectedDict() + + if series: + commits = series.commits + # Number the commits for test purposes + for commit in range(len(commits)): + commits[commit].sequence = commit + else: + commits = None + + Print(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) + 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 |