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 options.git_dir = os.path.join(options.git, '.git')
107 toolchains = toolchain.Toolchains()
108 toolchains.GetSettings()
109 toolchains.Scan(options.list_tool_chains)
110 if options.list_tool_chains:
115 # Work out how many commits to build. We want to build everything on the
116 # branch. We also build the upstream commit as a control so we can see
117 # problems introduced by the first commit on the branch.
118 col = terminal.Color()
119 count = options.count
121 if not options.branch:
124 count = gitutil.CountCommitsInBranch(options.git_dir,
127 str = ("Branch '%s' not found or has no upstream" %
129 sys.exit(col.Color(col.RED, str))
130 count += 1 # Build upstream commit also
133 str = ("No commits found to process in branch '%s': "
134 "set branch's upstream or use -c flag" % options.branch)
135 sys.exit(col.Color(col.RED, str))
137 # Work out what subset of the boards we are building
138 board_file = os.path.join(options.git, 'boards.cfg')
139 status = subprocess.call([os.path.join(options.git,
140 'tools/genboardscfg.py')])
142 sys.exit("Failed to generate boards.cfg")
144 boards = board.Boards()
145 boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
149 for arg in options.exclude:
150 exclude += arg.split(',')
152 why_selected = boards.SelectBoards(args, exclude)
153 selected = boards.GetSelected()
154 if not len(selected):
155 sys.exit(col.Color(col.RED, 'No matching boards found'))
157 # Read the metadata from the commits. First look at the upstream commit,
158 # then the ones in the branch. We would like to do something like
159 # upstream/master~..branch but that isn't possible if upstream/master is
160 # a merge commit (it will list all the commits that form part of the
164 range_expr = gitutil.GetRangeInBranch(options.git_dir,
166 upstream_commit = gitutil.GetUpstream(options.git_dir,
168 series = patchstream.GetMetaDataForList(upstream_commit,
171 # Conflicting tags are not a problem for buildman, since it does
172 # not use them. For example, Series-version is not useful for
173 # buildman. On the other hand conflicting tags will cause an
174 # error. So allow later tags to overwrite earlier ones.
175 series.allow_overwrite = True
176 series = patchstream.GetMetaDataForList(range_expr,
177 options.git_dir, None, series)
180 series = patchstream.GetMetaDataForList(options.branch,
181 options.git_dir, count)
184 options.verbose = True
185 options.show_errors = True
187 # By default we have one thread per CPU. But if there are not enough jobs
188 # we can have fewer threads and use a high '-j' value for make.
189 if not options.threads:
190 options.threads = min(multiprocessing.cpu_count(), len(selected))
192 options.jobs = max(1, (multiprocessing.cpu_count() +
193 len(selected) - 1) / len(selected))
196 options.step = len(series.commits) - 1
198 gnu_make = command.Output(os.path.join(options.git,
199 'scripts/show-gnu-make')).rstrip()
201 sys.exit('GNU Make not found')
203 # Create a new builder with the selected options
205 dirname = options.branch
208 output_dir = os.path.join(options.output_dir, dirname)
209 builder = Builder(toolchains, output_dir, options.git_dir,
210 options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
211 show_unknown=options.show_unknown, step=options.step)
212 builder.force_config_on_failure = not options.quick
214 builder.do_make = make_func
216 # For a dry run, just show our actions as a sanity check
218 ShowActions(series, why_selected, selected, builder, options)
220 builder.force_build = options.force_build
221 builder.force_build_failures = options.force_build_failures
222 builder.force_reconfig = options.force_reconfig
223 builder.in_tree = options.in_tree
225 # Work out which boards to build
226 board_selected = boards.GetSelectedDict()
229 commits = series.commits
233 Print(GetActionSummary(options.summary, commits, board_selected,
236 builder.SetDisplayOptions(options.show_errors, options.show_sizes,
237 options.show_detail, options.show_bloat,
238 options.list_error_boards)
240 # We can't show function sizes without board details at present
241 if options.show_bloat:
242 options.show_detail = True
243 builder.ShowSummary(commits, board_selected)
245 fail, warned = builder.BuildBoards(commits, board_selected,
246 options.keep_outputs, options.verbose)