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