]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/buildman/control.py
buildman: make sure to invoke GNU Make
[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
19 def GetPlural(count):
20     """Returns a plural 's' if count is not 1"""
21     return 's' if count != 1 else ''
22
23 def 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
37 def 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
69 def 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)
99         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)
103         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)
129     # 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
134     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
148     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
154     # Create a new builder with the selected options
155     output_dir = os.path.join(options.output_dir, options.branch)
156     builder = Builder(toolchains, output_dir, options.git_dir,
157             options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
158             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
166         builder.force_build_failures = options.force_build_failures
167         builder.force_reconfig = options.force_reconfig
168         builder.in_tree = options.in_tree
169
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)