blob: d2f4102ba729c92e8b4c356cd4d3bb76c1688216 [file] [log] [blame]
Simon Glassc05694f2013-04-03 11:07:16 +00001# Copyright (c) 2013 The Chromium OS Authors.
2#
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02003# SPDX-License-Identifier: GPL-2.0+
Simon Glassc05694f2013-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
17
18def GetPlural(count):
19 """Returns a plural 's' if count is not 1"""
20 return 's' if count != 1 else ''
21
22def GetActionSummary(is_summary, count, selected, options):
23 """Return a string summarising the intended action.
24
25 Returns:
26 Summary string.
27 """
28 count = (count + options.step - 1) / options.step
29 str = '%s %d commit%s for %d boards' % (
30 'Summary of' if is_summary else 'Building', count, GetPlural(count),
31 len(selected))
32 str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
33 GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
34 return str
35
36def ShowActions(series, why_selected, boards_selected, builder, options):
37 """Display a list of actions that we would take, if not a dry run.
38
39 Args:
40 series: Series object
41 why_selected: Dictionary where each key is a buildman argument
42 provided by the user, and the value is the boards brought
43 in by that argument. For example, 'arm' might bring in
44 400 boards, so in this case the key would be 'arm' and
45 the value would be a list of board names.
46 boards_selected: Dict of selected boards, key is target name,
47 value is Board object
48 builder: The builder that will be used to build the commits
49 options: Command line options object
50 """
51 col = terminal.Color()
52 print 'Dry run, so not doing much. But I would do this:'
53 print
54 print GetActionSummary(False, len(series.commits), boards_selected,
55 options)
56 print 'Build directory: %s' % builder.base_dir
57 for upto in range(0, len(series.commits), options.step):
58 commit = series.commits[upto]
59 print ' ', col.Color(col.YELLOW, commit.hash, bright=False),
60 print commit.subject
61 print
62 for arg in why_selected:
63 if arg != 'all':
64 print arg, ': %d boards' % why_selected[arg]
65 print ('Total boards to build for each commit: %d\n' %
66 why_selected['all'])
67
68def DoBuildman(options, args):
69 """The main control code for buildman
70
71 Args:
72 options: Command line options object
73 args: Command line arguments (list of strings)
74 """
75 gitutil.Setup()
76
77 bsettings.Setup()
78 options.git_dir = os.path.join(options.git, '.git')
79
80 toolchains = toolchain.Toolchains()
81 toolchains.Scan(options.list_tool_chains)
82 if options.list_tool_chains:
83 toolchains.List()
84 print
85 return
86
87 # Work out how many commits to build. We want to build everything on the
88 # branch. We also build the upstream commit as a control so we can see
89 # problems introduced by the first commit on the branch.
90 col = terminal.Color()
91 count = options.count
92 if count == -1:
93 if not options.branch:
94 str = 'Please use -b to specify a branch to build'
95 print col.Color(col.RED, str)
96 sys.exit(1)
97 count = gitutil.CountCommitsInBranch(options.git_dir, options.branch)
Simon Glassd2e95382013-05-08 08:06:08 +000098 if count is None:
99 str = "Branch '%s' not found or has no upstream" % options.branch
100 print col.Color(col.RED, str)
101 sys.exit(1)
Simon Glassc05694f2013-04-03 11:07:16 +0000102 count += 1 # Build upstream commit also
103
104 if not count:
105 str = ("No commits found to process in branch '%s': "
106 "set branch's upstream or use -c flag" % options.branch)
107 print col.Color(col.RED, str)
108 sys.exit(1)
109
110 # Work out what subset of the boards we are building
111 boards = board.Boards()
112 boards.ReadBoards(os.path.join(options.git, 'boards.cfg'))
113 why_selected = boards.SelectBoards(args)
114 selected = boards.GetSelected()
115 if not len(selected):
116 print col.Color(col.RED, 'No matching boards found')
117 sys.exit(1)
118
119 # Read the metadata from the commits. First look at the upstream commit,
120 # then the ones in the branch. We would like to do something like
121 # upstream/master~..branch but that isn't possible if upstream/master is
122 # a merge commit (it will list all the commits that form part of the
123 # merge)
124 range_expr = gitutil.GetRangeInBranch(options.git_dir, options.branch)
125 upstream_commit = gitutil.GetUpstream(options.git_dir, options.branch)
126 series = patchstream.GetMetaDataForList(upstream_commit, options.git_dir,
127 1)
Simon Glass29c5abc2013-05-02 14:46:02 +0000128 # Conflicting tags are not a problem for buildman, since it does not use
129 # them. For example, Series-version is not useful for buildman. On the
130 # other hand conflicting tags will cause an error. So allow later tags
131 # to overwrite earlier ones.
132 series.allow_overwrite = True
Simon Glassc05694f2013-04-03 11:07:16 +0000133 series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None,
134 series)
135
136 # By default we have one thread per CPU. But if there are not enough jobs
137 # we can have fewer threads and use a high '-j' value for make.
138 if not options.threads:
139 options.threads = min(multiprocessing.cpu_count(), len(selected))
140 if not options.jobs:
141 options.jobs = max(1, (multiprocessing.cpu_count() +
142 len(selected) - 1) / len(selected))
143
144 if not options.step:
145 options.step = len(series.commits) - 1
146
147 # Create a new builder with the selected options
Daniel Schwierzecka42bc302014-04-17 21:13:11 +0200148 output_dir = os.path.join(options.output_dir, options.branch)
Simon Glassc05694f2013-04-03 11:07:16 +0000149 builder = Builder(toolchains, output_dir, options.git_dir,
150 options.threads, options.jobs, checkout=True,
151 show_unknown=options.show_unknown, step=options.step)
152 builder.force_config_on_failure = not options.quick
153
154 # For a dry run, just show our actions as a sanity check
155 if options.dry_run:
156 ShowActions(series, why_selected, selected, builder, options)
157 else:
158 builder.force_build = options.force_build
159
160 # Work out which boards to build
161 board_selected = boards.GetSelectedDict()
162
163 print GetActionSummary(options.summary, count, board_selected, options)
164
165 if options.summary:
166 # We can't show function sizes without board details at present
167 if options.show_bloat:
168 options.show_detail = True
169 builder.ShowSummary(series.commits, board_selected,
170 options.show_errors, options.show_sizes,
171 options.show_detail, options.show_bloat)
172 else:
173 builder.BuildBoards(series.commits, board_selected,
174 options.show_errors, options.keep_outputs)