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