1# Copyright (c) 2013 The Chromium OS Authors. 2# 3# SPDX-License-Identifier: GPL-2.0+ 4# 5 6import multiprocessing 7import os 8import sys 9 10import board 11import bsettings 12from builder import Builder 13import gitutil 14import patchstream 15import terminal 16import toolchain 17 18def GetPlural(count): 19 """Returns a plural 's' if count is not 1""" 20 return 's' if count != 1 else '' 21 22def GetActionSummary(is_summary, count, selected, options): 23 """Return a string summarising the intended action. 24 25 Returns: 26 Summary string. 27 """ 28 count = (count + options.step - 1) / options.step 29 str = '%s %d commit%s for %d boards' % ( 30 'Summary of' if is_summary else 'Building', count, GetPlural(count), 31 len(selected)) 32 str += ' (%d thread%s, %d job%s per thread)' % (options.threads, 33 GetPlural(options.threads), options.jobs, GetPlural(options.jobs)) 34 return str 35 36def ShowActions(series, why_selected, boards_selected, builder, options): 37 """Display a list of actions that we would take, if not a dry run. 38 39 Args: 40 series: Series object 41 why_selected: Dictionary where each key is a buildman argument 42 provided by the user, and the value is the boards brought 43 in by that argument. For example, 'arm' might bring in 44 400 boards, so in this case the key would be 'arm' and 45 the value would be a list of board names. 46 boards_selected: Dict of selected boards, key is target name, 47 value is Board object 48 builder: The builder that will be used to build the commits 49 options: Command line options object 50 """ 51 col = terminal.Color() 52 print 'Dry run, so not doing much. But I would do this:' 53 print 54 print GetActionSummary(False, len(series.commits), boards_selected, 55 options) 56 print 'Build directory: %s' % builder.base_dir 57 for upto in range(0, len(series.commits), options.step): 58 commit = series.commits[upto] 59 print ' ', col.Color(col.YELLOW, commit.hash, bright=False), 60 print commit.subject 61 print 62 for arg in why_selected: 63 if arg != 'all': 64 print arg, ': %d boards' % why_selected[arg] 65 print ('Total boards to build for each commit: %d\n' % 66 why_selected['all']) 67 68def DoBuildman(options, args): 69 """The main control code for buildman 70 71 Args: 72 options: Command line options object 73 args: Command line arguments (list of strings) 74 """ 75 gitutil.Setup() 76 77 bsettings.Setup() 78 options.git_dir = os.path.join(options.git, '.git') 79 80 toolchains = toolchain.Toolchains() 81 toolchains.Scan(options.list_tool_chains) 82 if options.list_tool_chains: 83 toolchains.List() 84 print 85 return 86 87 # Work out how many commits to build. We want to build everything on the 88 # branch. We also build the upstream commit as a control so we can see 89 # problems introduced by the first commit on the branch. 90 col = terminal.Color() 91 count = options.count 92 if count == -1: 93 if not options.branch: 94 str = 'Please use -b to specify a branch to build' 95 print col.Color(col.RED, str) 96 sys.exit(1) 97 count = gitutil.CountCommitsInBranch(options.git_dir, options.branch) 98 if count is None: 99 str = "Branch '%s' not found or has no upstream" % options.branch 100 print col.Color(col.RED, str) 101 sys.exit(1) 102 count += 1 # Build upstream commit also 103 104 if not count: 105 str = ("No commits found to process in branch '%s': " 106 "set branch's upstream or use -c flag" % options.branch) 107 print col.Color(col.RED, str) 108 sys.exit(1) 109 110 # Work out what subset of the boards we are building 111 boards = board.Boards() 112 boards.ReadBoards(os.path.join(options.git, 'boards.cfg')) 113 why_selected = boards.SelectBoards(args) 114 selected = boards.GetSelected() 115 if not len(selected): 116 print col.Color(col.RED, 'No matching boards found') 117 sys.exit(1) 118 119 # Read the metadata from the commits. First look at the upstream commit, 120 # then the ones in the branch. We would like to do something like 121 # upstream/master~..branch but that isn't possible if upstream/master is 122 # a merge commit (it will list all the commits that form part of the 123 # merge) 124 range_expr = gitutil.GetRangeInBranch(options.git_dir, options.branch) 125 upstream_commit = gitutil.GetUpstream(options.git_dir, options.branch) 126 series = patchstream.GetMetaDataForList(upstream_commit, options.git_dir, 127 1) 128 # Conflicting tags are not a problem for buildman, since it does not use 129 # them. For example, Series-version is not useful for buildman. On the 130 # other hand conflicting tags will cause an error. So allow later tags 131 # to overwrite earlier ones. 132 series.allow_overwrite = True 133 series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None, 134 series) 135 136 # By default we have one thread per CPU. But if there are not enough jobs 137 # we can have fewer threads and use a high '-j' value for make. 138 if not options.threads: 139 options.threads = min(multiprocessing.cpu_count(), len(selected)) 140 if not options.jobs: 141 options.jobs = max(1, (multiprocessing.cpu_count() + 142 len(selected) - 1) / len(selected)) 143 144 if not options.step: 145 options.step = len(series.commits) - 1 146 147 # Create a new builder with the selected options 148 output_dir = os.path.join(options.output_dir, options.branch) 149 builder = Builder(toolchains, output_dir, options.git_dir, 150 options.threads, options.jobs, checkout=True, 151 show_unknown=options.show_unknown, step=options.step) 152 builder.force_config_on_failure = not options.quick 153 154 # For a dry run, just show our actions as a sanity check 155 if options.dry_run: 156 ShowActions(series, why_selected, selected, builder, options) 157 else: 158 builder.force_build = options.force_build 159 160 # Work out which boards to build 161 board_selected = boards.GetSelectedDict() 162 163 print GetActionSummary(options.summary, count, board_selected, options) 164 165 if options.summary: 166 # We can't show function sizes without board details at present 167 if options.show_bloat: 168 options.show_detail = True 169 builder.ShowSummary(series.commits, board_selected, 170 options.show_errors, options.show_sizes, 171 options.show_detail, options.show_bloat) 172 else: 173 builder.BuildBoards(series.commits, board_selected, 174 options.show_errors, options.keep_outputs) 175