]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/buildman/control.py
buildman: Refactor output options
[karo-tx-uboot.git] / tools / buildman / control.py
1 # Copyright (c) 2013 The Chromium OS Authors.
2 #
3 # SPDX-License-Identifier:      GPL-2.0+
4 #
5
6 import multiprocessing
7 import os
8 import sys
9
10 import board
11 import bsettings
12 from builder import Builder
13 import gitutil
14 import patchstream
15 import terminal
16 import toolchain
17 import command
18 import subprocess
19
20 def GetPlural(count):
21     """Returns a plural 's' if count is not 1"""
22     return 's' if count != 1 else ''
23
24 def GetActionSummary(is_summary, commits, selected, options):
25     """Return a string summarising the intended action.
26
27     Returns:
28         Summary string.
29     """
30     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,
38         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
43 def 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
61     if series:
62         commits = series.commits
63     else:
64         commits = None
65     print GetActionSummary(False, commits, boards_selected,
66             options)
67     print 'Build directory: %s' % builder.base_dir
68     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
73     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
80 def 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:
106             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
116
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
124     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
133     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)
146     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
161
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
173     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
179     # Create a new builder with the selected options
180     if options.branch:
181         dirname = options.branch
182     else:
183         dirname = 'current'
184     output_dir = os.path.join(options.output_dir, dirname)
185     builder = Builder(toolchains, output_dir, options.git_dir,
186             options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
187             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
195         builder.force_build_failures = options.force_build_failures
196         builder.force_reconfig = options.force_reconfig
197         builder.in_tree = options.in_tree
198
199         # Work out which boards to build
200         board_selected = boards.GetSelectedDict()
201
202         if series:
203             commits = series.commits
204         else:
205             commits = None
206
207         print GetActionSummary(options.summary, commits, board_selected,
208                                options)
209
210         builder.SetDisplayOptions(options.show_errors, options.show_sizes,
211                                   options.show_detail, options.show_bloat)
212         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
216             builder.ShowSummary(commits, board_selected)
217         else:
218             builder.BuildBoards(commits, board_selected,
219                                 options.keep_outputs)