1 # Copyright (c) 2013 The Chromium OS Authors.
3 # SPDX-License-Identifier: GPL-2.0+
12 from builder import Builder
16 from terminal import Print
22 """Returns a plural 's' if count is not 1"""
23 return 's' if count != 1 else ''
25 def GetActionSummary(is_summary, commits, selected, options):
26 """Return a string summarising the intended action.
33 count = (count + options.step - 1) / options.step
34 commit_str = '%d commit%s' % (count, GetPlural(count))
36 commit_str = 'current source'
37 str = '%s %s for %d boards' % (
38 'Summary of' if is_summary else 'Building', commit_str,
40 str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
41 GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
44 def ShowActions(series, why_selected, boards_selected, builder, options):
45 """Display a list of actions that we would take, if not a dry run.
49 why_selected: Dictionary where each key is a buildman argument
50 provided by the user, and the value is the boards brought
51 in by that argument. For example, 'arm' might bring in
52 400 boards, so in this case the key would be 'arm' and
53 the value would be a list of board names.
54 boards_selected: Dict of selected boards, key is target name,
56 builder: The builder that will be used to build the commits
57 options: Command line options object
59 col = terminal.Color()
60 print 'Dry run, so not doing much. But I would do this:'
63 commits = series.commits
66 print GetActionSummary(False, commits, boards_selected,
68 print 'Build directory: %s' % builder.base_dir
70 for upto in range(0, len(series.commits), options.step):
71 commit = series.commits[upto]
72 print ' ', col.Color(col.YELLOW, commit.hash, bright=False),
75 for arg in why_selected:
77 print arg, ': %d boards' % why_selected[arg]
78 print ('Total boards to build for each commit: %d\n' %
81 def DoBuildman(options, args, toolchains=None, make_func=None):
82 """The main control code for buildman
85 options: Command line options object
86 args: Command line arguments (list of strings)
87 toolchains: Toolchains to use - this should be a Toolchains()
88 object. If None, then it will be created and scanned
89 make_func: Make function to use for the builder. This is called
90 to execute 'make'. If this is None, the normal function
91 will be used, which calls the 'make' tool with suitable
92 arguments. This setting is useful for tests.
95 pager = os.getenv('PAGER')
98 fname = os.path.join(os.path.dirname(sys.argv[0]), 'README')
99 command.Run(pager, fname)
104 bsettings.Setup(options.config_file)
105 options.git_dir = os.path.join(options.git, '.git')
108 toolchains = toolchain.Toolchains()
109 toolchains.GetSettings()
110 toolchains.Scan(options.list_tool_chains)
111 if options.list_tool_chains:
116 # Work out how many commits to build. We want to build everything on the
117 # branch. We also build the upstream commit as a control so we can see
118 # problems introduced by the first commit on the branch.
119 col = terminal.Color()
120 count = options.count
122 if not options.branch:
125 count = gitutil.CountCommitsInBranch(options.git_dir,
128 str = ("Branch '%s' not found or has no upstream" %
130 sys.exit(col.Color(col.RED, str))
131 count += 1 # Build upstream commit also
134 str = ("No commits found to process in branch '%s': "
135 "set branch's upstream or use -c flag" % options.branch)
136 sys.exit(col.Color(col.RED, str))
138 # Work out what subset of the boards we are building
139 board_file = os.path.join(options.git, 'boards.cfg')
140 status = subprocess.call([os.path.join(options.git,
141 'tools/genboardscfg.py')])
143 sys.exit("Failed to generate boards.cfg")
145 boards = board.Boards()
146 boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
150 for arg in options.exclude:
151 exclude += arg.split(',')
153 why_selected = boards.SelectBoards(args, exclude)
154 selected = boards.GetSelected()
155 if not len(selected):
156 sys.exit(col.Color(col.RED, 'No matching boards found'))
158 # Read the metadata from the commits. First look at the upstream commit,
159 # then the ones in the branch. We would like to do something like
160 # upstream/master~..branch but that isn't possible if upstream/master is
161 # a merge commit (it will list all the commits that form part of the
165 range_expr = gitutil.GetRangeInBranch(options.git_dir,
167 upstream_commit = gitutil.GetUpstream(options.git_dir,
169 series = patchstream.GetMetaDataForList(upstream_commit,
172 # Conflicting tags are not a problem for buildman, since it does
173 # not use them. For example, Series-version is not useful for
174 # buildman. On the other hand conflicting tags will cause an
175 # error. So allow later tags to overwrite earlier ones.
176 series.allow_overwrite = True
177 series = patchstream.GetMetaDataForList(range_expr,
178 options.git_dir, None, series)
181 series = patchstream.GetMetaDataForList(options.branch,
182 options.git_dir, count)
185 options.verbose = True
186 options.show_errors = True
188 # By default we have one thread per CPU. But if there are not enough jobs
189 # we can have fewer threads and use a high '-j' value for make.
190 if not options.threads:
191 options.threads = min(multiprocessing.cpu_count(), len(selected))
193 options.jobs = max(1, (multiprocessing.cpu_count() +
194 len(selected) - 1) / len(selected))
197 options.step = len(series.commits) - 1
199 gnu_make = command.Output(os.path.join(options.git,
200 'scripts/show-gnu-make')).rstrip()
202 sys.exit('GNU Make not found')
204 # Create a new builder with the selected options
206 dirname = options.branch
209 output_dir = os.path.join(options.output_dir, dirname)
210 builder = Builder(toolchains, output_dir, options.git_dir,
211 options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
212 show_unknown=options.show_unknown, step=options.step)
213 builder.force_config_on_failure = not options.quick
215 builder.do_make = make_func
217 # For a dry run, just show our actions as a sanity check
219 ShowActions(series, why_selected, selected, builder, options)
221 builder.force_build = options.force_build
222 builder.force_build_failures = options.force_build_failures
223 builder.force_reconfig = options.force_reconfig
224 builder.in_tree = options.in_tree
226 # Work out which boards to build
227 board_selected = boards.GetSelectedDict()
230 commits = series.commits
234 Print(GetActionSummary(options.summary, commits, board_selected,
237 builder.SetDisplayOptions(options.show_errors, options.show_sizes,
238 options.show_detail, options.show_bloat,
239 options.list_error_boards)
241 # We can't show function sizes without board details at present
242 if options.show_bloat:
243 options.show_detail = True
244 builder.ShowSummary(commits, board_selected)
246 fail, warned = builder.BuildBoards(commits, board_selected,
247 options.keep_outputs, options.verbose)