blob: d8fa74b90e8533e03a6d0ced0f0ae5508fd932e8 [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 """
87 gitutil.Setup()
88
89 bsettings.Setup()
90 options.git_dir = os.path.join(options.git, '.git')
91
92 toolchains = toolchain.Toolchains()
93 toolchains.Scan(options.list_tool_chains)
94 if options.list_tool_chains:
95 toolchains.List()
96 print
97 return
98
99 # Work out how many commits to build. We want to build everything on the
100 # branch. We also build the upstream commit as a control so we can see
101 # problems introduced by the first commit on the branch.
102 col = terminal.Color()
103 count = options.count
104 if count == -1:
105 if not options.branch:
Simon Glassfea58582014-08-09 15:32:59 -0600106 count = 1
107 else:
108 count = gitutil.CountCommitsInBranch(options.git_dir,
109 options.branch)
110 if count is None:
111 str = ("Branch '%s' not found or has no upstream" %
112 options.branch)
113 print col.Color(col.RED, str)
114 sys.exit(1)
115 count += 1 # Build upstream commit also
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000116
117 if not count:
118 str = ("No commits found to process in branch '%s': "
119 "set branch's upstream or use -c flag" % options.branch)
120 print col.Color(col.RED, str)
121 sys.exit(1)
122
123 # Work out what subset of the boards we are building
Masahiro Yamada73f30b92014-07-30 14:08:22 +0900124 board_file = os.path.join(options.git, 'boards.cfg')
125 if not os.path.exists(board_file):
126 print 'Could not find %s' % board_file
127 status = subprocess.call([os.path.join(options.git,
128 'tools/genboardscfg.py')])
129 if status != 0:
130 print >> sys.stderr, "Failed to generate boards.cfg"
131 sys.exit(1)
132
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000133 boards = board.Boards()
134 boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
135 why_selected = boards.SelectBoards(args)
136 selected = boards.GetSelected()
137 if not len(selected):
138 print col.Color(col.RED, 'No matching boards found')
139 sys.exit(1)
140
141 # Read the metadata from the commits. First look at the upstream commit,
142 # then the ones in the branch. We would like to do something like
143 # upstream/master~..branch but that isn't possible if upstream/master is
144 # a merge commit (it will list all the commits that form part of the
145 # merge)
Simon Glassfea58582014-08-09 15:32:59 -0600146 if options.branch:
147 range_expr = gitutil.GetRangeInBranch(options.git_dir, options.branch)
148 upstream_commit = gitutil.GetUpstream(options.git_dir, options.branch)
149 series = patchstream.GetMetaDataForList(upstream_commit,
150 options.git_dir, 1)
151
152 # Conflicting tags are not a problem for buildman, since it does not
153 # use them. For example, Series-version is not useful for buildman. On
154 # the other hand conflicting tags will cause an error. So allow later
155 # tags to overwrite earlier ones.
156 series.allow_overwrite = True
157 series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None,
158 series)
159 else:
160 series = None
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000161
162 # By default we have one thread per CPU. But if there are not enough jobs
163 # we can have fewer threads and use a high '-j' value for make.
164 if not options.threads:
165 options.threads = min(multiprocessing.cpu_count(), len(selected))
166 if not options.jobs:
167 options.jobs = max(1, (multiprocessing.cpu_count() +
168 len(selected) - 1) / len(selected))
169
170 if not options.step:
171 options.step = len(series.commits) - 1
172
Masahiro Yamada99796922014-07-22 11:19:09 +0900173 gnu_make = command.Output(os.path.join(options.git,
174 'scripts/show-gnu-make')).rstrip()
175 if not gnu_make:
176 print >> sys.stderr, 'GNU Make not found'
177 sys.exit(1)
178
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000179 # Create a new builder with the selected options
Simon Glassfea58582014-08-09 15:32:59 -0600180 if options.branch:
181 dirname = options.branch
182 else:
183 dirname = 'current'
184 output_dir = os.path.join(options.output_dir, dirname)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000185 builder = Builder(toolchains, output_dir, options.git_dir,
Masahiro Yamada99796922014-07-22 11:19:09 +0900186 options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000187 show_unknown=options.show_unknown, step=options.step)
188 builder.force_config_on_failure = not options.quick
189
190 # For a dry run, just show our actions as a sanity check
191 if options.dry_run:
192 ShowActions(series, why_selected, selected, builder, options)
193 else:
194 builder.force_build = options.force_build
Simon Glass4266dc22014-07-13 12:22:31 -0600195 builder.force_build_failures = options.force_build_failures
Simon Glass97e91522014-07-14 17:51:02 -0600196 builder.force_reconfig = options.force_reconfig
Simon Glass189a4962014-07-14 17:51:03 -0600197 builder.in_tree = options.in_tree
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000198
199 # Work out which boards to build
200 board_selected = boards.GetSelectedDict()
201
Simon Glassfea58582014-08-09 15:32:59 -0600202 if series:
203 commits = series.commits
204 else:
205 commits = None
206
207 print GetActionSummary(options.summary, commits, board_selected,
208 options)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000209
Simon Glassb2ea7ab2014-08-09 15:33:02 -0600210 builder.SetDisplayOptions(options.show_errors, options.show_sizes,
211 options.show_detail, options.show_bloat)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000212 if options.summary:
213 # We can't show function sizes without board details at present
214 if options.show_bloat:
215 options.show_detail = True
Simon Glassb2ea7ab2014-08-09 15:33:02 -0600216 builder.ShowSummary(commits, board_selected)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000217 else:
Simon Glassfea58582014-08-09 15:32:59 -0600218 builder.BuildBoards(commits, board_selected,
Simon Glassb2ea7ab2014-08-09 15:33:02 -0600219 options.keep_outputs)