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, boards=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.
93 board: Boards() object to use, containing a list of available
94 boards. If this is None it will be created and scanned.
97 pager = os.getenv('PAGER')
100 fname = os.path.join(os.path.dirname(sys.argv[0]), 'README')
101 command.Run(pager, fname)
106 options.git_dir = os.path.join(options.git, '.git')
109 toolchains = toolchain.Toolchains()
110 toolchains.GetSettings()
111 toolchains.Scan(options.list_tool_chains)
112 if options.list_tool_chains:
117 # Work out how many commits to build. We want to build everything on the
118 # branch. We also build the upstream commit as a control so we can see
119 # problems introduced by the first commit on the branch.
120 col = terminal.Color()
121 count = options.count
123 if not options.branch:
126 count = gitutil.CountCommitsInBranch(options.git_dir,
129 str = ("Branch '%s' not found or has no upstream" %
131 sys.exit(col.Color(col.RED, str))
132 count += 1 # Build upstream commit also
135 str = ("No commits found to process in branch '%s': "
136 "set branch's upstream or use -c flag" % options.branch)
137 sys.exit(col.Color(col.RED, str))
139 # Work out what subset of the boards we are building
141 board_file = os.path.join(options.git, 'boards.cfg')
142 status = subprocess.call([os.path.join(options.git,
143 'tools/genboardscfg.py')])
145 sys.exit("Failed to generate boards.cfg")
147 boards = board.Boards()
148 boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
152 for arg in options.exclude:
153 exclude += arg.split(',')
155 why_selected = boards.SelectBoards(args, exclude)
156 selected = boards.GetSelected()
157 if not len(selected):
158 sys.exit(col.Color(col.RED, 'No matching boards found'))
160 # Read the metadata from the commits. First look at the upstream commit,
161 # then the ones in the branch. We would like to do something like
162 # upstream/master~..branch but that isn't possible if upstream/master is
163 # a merge commit (it will list all the commits that form part of the
167 range_expr = gitutil.GetRangeInBranch(options.git_dir,
169 upstream_commit = gitutil.GetUpstream(options.git_dir,
171 series = patchstream.GetMetaDataForList(upstream_commit,
174 # Conflicting tags are not a problem for buildman, since it does
175 # not use them. For example, Series-version is not useful for
176 # buildman. On the other hand conflicting tags will cause an
177 # error. So allow later tags to overwrite earlier ones.
178 series.allow_overwrite = True
179 series = patchstream.GetMetaDataForList(range_expr,
180 options.git_dir, None, series)
183 series = patchstream.GetMetaDataForList(options.branch,
184 options.git_dir, count)
187 options.verbose = True
188 options.show_errors = True
190 # By default we have one thread per CPU. But if there are not enough jobs
191 # we can have fewer threads and use a high '-j' value for make.
192 if not options.threads:
193 options.threads = min(multiprocessing.cpu_count(), len(selected))
195 options.jobs = max(1, (multiprocessing.cpu_count() +
196 len(selected) - 1) / len(selected))
199 options.step = len(series.commits) - 1
201 gnu_make = command.Output(os.path.join(options.git,
202 'scripts/show-gnu-make')).rstrip()
204 sys.exit('GNU Make not found')
206 # Create a new builder with the selected options
208 dirname = options.branch
211 output_dir = os.path.join(options.output_dir, dirname)
212 builder = Builder(toolchains, output_dir, options.git_dir,
213 options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
214 show_unknown=options.show_unknown, step=options.step)
215 builder.force_config_on_failure = not options.quick
217 builder.do_make = make_func
219 # For a dry run, just show our actions as a sanity check
221 ShowActions(series, why_selected, selected, builder, options)
223 builder.force_build = options.force_build
224 builder.force_build_failures = options.force_build_failures
225 builder.force_reconfig = options.force_reconfig
226 builder.in_tree = options.in_tree
228 # Work out which boards to build
229 board_selected = boards.GetSelectedDict()
232 commits = series.commits
236 Print(GetActionSummary(options.summary, commits, board_selected,
239 builder.SetDisplayOptions(options.show_errors, options.show_sizes,
240 options.show_detail, options.show_bloat,
241 options.list_error_boards)
243 # We can't show function sizes without board details at present
244 if options.show_bloat:
245 options.show_detail = True
246 builder.ShowSummary(commits, board_selected)
248 fail, warned = builder.BuildBoards(commits, board_selected,
249 options.keep_outputs, options.verbose)