blob: 408d9b126bb7a7d98e89bccc6923fd7f02d5833f [file] [log] [blame]
Simon Glassfc3fe1c2013-04-03 11:07:16 +00001# Copyright (c) 2013 The Chromium OS Authors.
2#
Wolfgang Denk1a459662013-07-08 09:37:19 +02003# SPDX-License-Identifier: GPL-2.0+
Simon Glassfc3fe1c2013-04-03 11:07:16 +00004#
5
6import multiprocessing
7import os
8import sys
9
10import board
11import bsettings
12from builder import Builder
13import gitutil
14import patchstream
15import terminal
16import toolchain
Masahiro Yamada99796922014-07-22 11:19:09 +090017import command
Masahiro Yamada73f30b92014-07-30 14:08:22 +090018import subprocess
Simon Glassfc3fe1c2013-04-03 11:07:16 +000019
20def GetPlural(count):
21 """Returns a plural 's' if count is not 1"""
22 return 's' if count != 1 else ''
23
Simon Glassfea58582014-08-09 15:32:59 -060024def GetActionSummary(is_summary, commits, selected, options):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000025 """Return a string summarising the intended action.
26
27 Returns:
28 Summary string.
29 """
Simon Glassfea58582014-08-09 15:32:59 -060030 if commits:
31 count = len(commits)
32 count = (count + options.step - 1) / options.step
33 commit_str = '%d commit%s' % (count, GetPlural(count))
34 else:
35 commit_str = 'current source'
36 str = '%s %s for %d boards' % (
37 'Summary of' if is_summary else 'Building', commit_str,
Simon Glassfc3fe1c2013-04-03 11:07:16 +000038 len(selected))
39 str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
40 GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
41 return str
42
43def ShowActions(series, why_selected, boards_selected, builder, options):
44 """Display a list of actions that we would take, if not a dry run.
45
46 Args:
47 series: Series object
48 why_selected: Dictionary where each key is a buildman argument
49 provided by the user, and the value is the boards brought
50 in by that argument. For example, 'arm' might bring in
51 400 boards, so in this case the key would be 'arm' and
52 the value would be a list of board names.
53 boards_selected: Dict of selected boards, key is target name,
54 value is Board object
55 builder: The builder that will be used to build the commits
56 options: Command line options object
57 """
58 col = terminal.Color()
59 print 'Dry run, so not doing much. But I would do this:'
60 print
Simon Glassfea58582014-08-09 15:32:59 -060061 if series:
62 commits = series.commits
63 else:
64 commits = None
65 print GetActionSummary(False, commits, boards_selected,
Simon Glassfc3fe1c2013-04-03 11:07:16 +000066 options)
67 print 'Build directory: %s' % builder.base_dir
Simon Glassfea58582014-08-09 15:32:59 -060068 if commits:
69 for upto in range(0, len(series.commits), options.step):
70 commit = series.commits[upto]
71 print ' ', col.Color(col.YELLOW, commit.hash, bright=False),
72 print commit.subject
Simon Glassfc3fe1c2013-04-03 11:07:16 +000073 print
74 for arg in why_selected:
75 if arg != 'all':
76 print arg, ': %d boards' % why_selected[arg]
77 print ('Total boards to build for each commit: %d\n' %
78 why_selected['all'])
79
80def DoBuildman(options, args):
81 """The main control code for buildman
82
83 Args:
84 options: Command line options object
85 args: Command line arguments (list of strings)
86 """
Simon Glass48ba5852014-09-05 19:00:11 -060087 if options.full_help:
88 pager = os.getenv('PAGER')
89 if not pager:
90 pager = 'more'
91 fname = os.path.join(os.path.dirname(sys.argv[0]), 'README')
92 command.Run(pager, fname)
93 return 0
94
Simon Glassfc3fe1c2013-04-03 11:07:16 +000095 gitutil.Setup()
96
Simon Glass0f7c9dd2014-08-09 15:33:05 -060097 bsettings.Setup(options.config_file)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000098 options.git_dir = os.path.join(options.git, '.git')
99
100 toolchains = toolchain.Toolchains()
101 toolchains.Scan(options.list_tool_chains)
102 if options.list_tool_chains:
103 toolchains.List()
104 print
Simon Glass2c3deb92014-08-28 09:43:39 -0600105 return 0
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000106
107 # Work out how many commits to build. We want to build everything on the
108 # branch. We also build the upstream commit as a control so we can see
109 # problems introduced by the first commit on the branch.
110 col = terminal.Color()
111 count = options.count
112 if count == -1:
113 if not options.branch:
Simon Glassfea58582014-08-09 15:32:59 -0600114 count = 1
115 else:
116 count = gitutil.CountCommitsInBranch(options.git_dir,
117 options.branch)
118 if count is None:
119 str = ("Branch '%s' not found or has no upstream" %
120 options.branch)
Masahiro Yamada31e21412014-08-16 00:59:26 +0900121 sys.exit(col.Color(col.RED, str))
Simon Glassfea58582014-08-09 15:32:59 -0600122 count += 1 # Build upstream commit also
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000123
124 if not count:
125 str = ("No commits found to process in branch '%s': "
126 "set branch's upstream or use -c flag" % options.branch)
Masahiro Yamada31e21412014-08-16 00:59:26 +0900127 sys.exit(col.Color(col.RED, str))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000128
129 # Work out what subset of the boards we are building
Masahiro Yamada73f30b92014-07-30 14:08:22 +0900130 board_file = os.path.join(options.git, 'boards.cfg')
Masahiro Yamada390f7032014-08-22 14:33:41 +0900131 status = subprocess.call([os.path.join(options.git,
132 'tools/genboardscfg.py')])
133 if status != 0:
134 sys.exit("Failed to generate boards.cfg")
Masahiro Yamada73f30b92014-07-30 14:08:22 +0900135
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000136 boards = board.Boards()
137 boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
Simon Glass3cf4ae62014-08-28 09:43:41 -0600138
139 exclude = []
140 if options.exclude:
141 for arg in options.exclude:
142 exclude += arg.split(',')
143
144 why_selected = boards.SelectBoards(args, exclude)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000145 selected = boards.GetSelected()
146 if not len(selected):
Masahiro Yamada31e21412014-08-16 00:59:26 +0900147 sys.exit(col.Color(col.RED, 'No matching boards found'))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000148
149 # Read the metadata from the commits. First look at the upstream commit,
150 # then the ones in the branch. We would like to do something like
151 # upstream/master~..branch but that isn't possible if upstream/master is
152 # a merge commit (it will list all the commits that form part of the
153 # merge)
Simon Glassfea58582014-08-09 15:32:59 -0600154 if options.branch:
Simon Glass3b74ba52014-08-09 15:33:09 -0600155 if count == -1:
156 range_expr = gitutil.GetRangeInBranch(options.git_dir,
157 options.branch)
158 upstream_commit = gitutil.GetUpstream(options.git_dir,
159 options.branch)
160 series = patchstream.GetMetaDataForList(upstream_commit,
161 options.git_dir, 1)
Simon Glassfea58582014-08-09 15:32:59 -0600162
Simon Glass3b74ba52014-08-09 15:33:09 -0600163 # Conflicting tags are not a problem for buildman, since it does
164 # not use them. For example, Series-version is not useful for
165 # buildman. On the other hand conflicting tags will cause an
166 # error. So allow later tags to overwrite earlier ones.
167 series.allow_overwrite = True
168 series = patchstream.GetMetaDataForList(range_expr,
169 options.git_dir, None, series)
170 else:
171 # Honour the count
172 series = patchstream.GetMetaDataForList(options.branch,
173 options.git_dir, count)
Simon Glassfea58582014-08-09 15:32:59 -0600174 else:
175 series = None
Simon Glasse5a0e5d2014-08-09 15:33:03 -0600176 options.verbose = True
177 options.show_errors = True
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000178
179 # By default we have one thread per CPU. But if there are not enough jobs
180 # we can have fewer threads and use a high '-j' value for make.
181 if not options.threads:
182 options.threads = min(multiprocessing.cpu_count(), len(selected))
183 if not options.jobs:
184 options.jobs = max(1, (multiprocessing.cpu_count() +
185 len(selected) - 1) / len(selected))
186
187 if not options.step:
188 options.step = len(series.commits) - 1
189
Masahiro Yamada99796922014-07-22 11:19:09 +0900190 gnu_make = command.Output(os.path.join(options.git,
191 'scripts/show-gnu-make')).rstrip()
192 if not gnu_make:
Masahiro Yamada31e21412014-08-16 00:59:26 +0900193 sys.exit('GNU Make not found')
Masahiro Yamada99796922014-07-22 11:19:09 +0900194
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000195 # Create a new builder with the selected options
Simon Glassfea58582014-08-09 15:32:59 -0600196 if options.branch:
197 dirname = options.branch
198 else:
199 dirname = 'current'
200 output_dir = os.path.join(options.output_dir, dirname)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000201 builder = Builder(toolchains, output_dir, options.git_dir,
Masahiro Yamada99796922014-07-22 11:19:09 +0900202 options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000203 show_unknown=options.show_unknown, step=options.step)
204 builder.force_config_on_failure = not options.quick
205
206 # For a dry run, just show our actions as a sanity check
207 if options.dry_run:
208 ShowActions(series, why_selected, selected, builder, options)
209 else:
210 builder.force_build = options.force_build
Simon Glass4266dc22014-07-13 12:22:31 -0600211 builder.force_build_failures = options.force_build_failures
Simon Glass97e91522014-07-14 17:51:02 -0600212 builder.force_reconfig = options.force_reconfig
Simon Glass189a4962014-07-14 17:51:03 -0600213 builder.in_tree = options.in_tree
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000214
215 # Work out which boards to build
216 board_selected = boards.GetSelectedDict()
217
Simon Glassfea58582014-08-09 15:32:59 -0600218 if series:
219 commits = series.commits
220 else:
221 commits = None
222
223 print GetActionSummary(options.summary, commits, board_selected,
224 options)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000225
Simon Glassb2ea7ab2014-08-09 15:33:02 -0600226 builder.SetDisplayOptions(options.show_errors, options.show_sizes,
Simon Glassed966652014-08-28 09:43:43 -0600227 options.show_detail, options.show_bloat,
228 options.list_error_boards)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000229 if options.summary:
230 # We can't show function sizes without board details at present
231 if options.show_bloat:
232 options.show_detail = True
Simon Glassb2ea7ab2014-08-09 15:33:02 -0600233 builder.ShowSummary(commits, board_selected)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000234 else:
Simon Glass2c3deb92014-08-28 09:43:39 -0600235 fail, warned = builder.BuildBoards(commits, board_selected,
Simon Glasse5a0e5d2014-08-09 15:33:03 -0600236 options.keep_outputs, options.verbose)
Simon Glass2c3deb92014-08-28 09:43:39 -0600237 if fail:
238 return 128
239 elif warned:
240 return 129
241 return 0