1 # Copyright (c) 2013 The Chromium OS Authors.
3 # SPDX-License-Identifier: GPL-2.0+
12 from builder import Builder
21 """Returns a plural 's' if count is not 1"""
22 return 's' if count != 1 else ''
24 def GetActionSummary(is_summary, count, selected, options):
25 """Return a string summarising the intended action.
30 count = (count + options.step - 1) / options.step
31 str = '%s %d commit%s for %d boards' % (
32 'Summary of' if is_summary else 'Building', count, GetPlural(count),
34 str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
35 GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
38 def ShowActions(series, why_selected, boards_selected, builder, options):
39 """Display a list of actions that we would take, if not a dry run.
43 why_selected: Dictionary where each key is a buildman argument
44 provided by the user, and the value is the boards brought
45 in by that argument. For example, 'arm' might bring in
46 400 boards, so in this case the key would be 'arm' and
47 the value would be a list of board names.
48 boards_selected: Dict of selected boards, key is target name,
50 builder: The builder that will be used to build the commits
51 options: Command line options object
53 col = terminal.Color()
54 print 'Dry run, so not doing much. But I would do this:'
56 print GetActionSummary(False, len(series.commits), boards_selected,
58 print 'Build directory: %s' % builder.base_dir
59 for upto in range(0, len(series.commits), options.step):
60 commit = series.commits[upto]
61 print ' ', col.Color(col.YELLOW, commit.hash, bright=False),
64 for arg in why_selected:
66 print arg, ': %d boards' % why_selected[arg]
67 print ('Total boards to build for each commit: %d\n' %
70 def DoBuildman(options, args):
71 """The main control code for buildman
74 options: Command line options object
75 args: Command line arguments (list of strings)
80 options.git_dir = os.path.join(options.git, '.git')
82 toolchains = toolchain.Toolchains()
83 toolchains.Scan(options.list_tool_chains)
84 if options.list_tool_chains:
89 # Work out how many commits to build. We want to build everything on the
90 # branch. We also build the upstream commit as a control so we can see
91 # problems introduced by the first commit on the branch.
92 col = terminal.Color()
95 if not options.branch:
96 str = 'Please use -b to specify a branch to build'
97 print col.Color(col.RED, str)
99 count = gitutil.CountCommitsInBranch(options.git_dir, options.branch)
101 str = "Branch '%s' not found or has no upstream" % options.branch
102 print col.Color(col.RED, str)
104 count += 1 # Build upstream commit also
107 str = ("No commits found to process in branch '%s': "
108 "set branch's upstream or use -c flag" % options.branch)
109 print col.Color(col.RED, str)
112 # Work out what subset of the boards we are building
113 board_file = os.path.join(options.git, 'boards.cfg')
114 if not os.path.exists(board_file):
115 print 'Could not find %s' % board_file
116 status = subprocess.call([os.path.join(options.git,
117 'tools/genboardscfg.py')])
119 print >> sys.stderr, "Failed to generate boards.cfg"
122 boards = board.Boards()
123 boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
124 why_selected = boards.SelectBoards(args)
125 selected = boards.GetSelected()
126 if not len(selected):
127 print col.Color(col.RED, 'No matching boards found')
130 # Read the metadata from the commits. First look at the upstream commit,
131 # then the ones in the branch. We would like to do something like
132 # upstream/master~..branch but that isn't possible if upstream/master is
133 # a merge commit (it will list all the commits that form part of the
135 range_expr = gitutil.GetRangeInBranch(options.git_dir, options.branch)
136 upstream_commit = gitutil.GetUpstream(options.git_dir, options.branch)
137 series = patchstream.GetMetaDataForList(upstream_commit, options.git_dir,
139 # Conflicting tags are not a problem for buildman, since it does not use
140 # them. For example, Series-version is not useful for buildman. On the
141 # other hand conflicting tags will cause an error. So allow later tags
142 # to overwrite earlier ones.
143 series.allow_overwrite = True
144 series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None,
147 # By default we have one thread per CPU. But if there are not enough jobs
148 # we can have fewer threads and use a high '-j' value for make.
149 if not options.threads:
150 options.threads = min(multiprocessing.cpu_count(), len(selected))
152 options.jobs = max(1, (multiprocessing.cpu_count() +
153 len(selected) - 1) / len(selected))
156 options.step = len(series.commits) - 1
158 gnu_make = command.Output(os.path.join(options.git,
159 'scripts/show-gnu-make')).rstrip()
161 print >> sys.stderr, 'GNU Make not found'
164 # Create a new builder with the selected options
165 output_dir = os.path.join(options.output_dir, options.branch)
166 builder = Builder(toolchains, output_dir, options.git_dir,
167 options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
168 show_unknown=options.show_unknown, step=options.step)
169 builder.force_config_on_failure = not options.quick
171 # For a dry run, just show our actions as a sanity check
173 ShowActions(series, why_selected, selected, builder, options)
175 builder.force_build = options.force_build
176 builder.force_build_failures = options.force_build_failures
177 builder.force_reconfig = options.force_reconfig
178 builder.in_tree = options.in_tree
180 # Work out which boards to build
181 board_selected = boards.GetSelectedDict()
183 print GetActionSummary(options.summary, count, board_selected, options)
186 # We can't show function sizes without board details at present
187 if options.show_bloat:
188 options.show_detail = True
189 builder.ShowSummary(series.commits, board_selected,
190 options.show_errors, options.show_sizes,
191 options.show_detail, options.show_bloat)
193 builder.BuildBoards(series.commits, board_selected,
194 options.show_errors, options.keep_outputs)