1 # Copyright (c) 2013 The Chromium OS Authors.
3 # SPDX-License-Identifier: GPL-2.0+
12 from builder import Builder
19 """Returns a plural 's' if count is not 1"""
20 return 's' if count != 1 else ''
22 def GetActionSummary(is_summary, count, selected, options):
23 """Return a string summarising the intended action.
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),
32 str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
33 GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
36 def ShowActions(series, why_selected, boards_selected, builder, options):
37 """Display a list of actions that we would take, if not a dry run.
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,
48 builder: The builder that will be used to build the commits
49 options: Command line options object
51 col = terminal.Color()
52 print 'Dry run, so not doing much. But I would do this:'
54 print GetActionSummary(False, len(series.commits), boards_selected,
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),
62 for arg in why_selected:
64 print arg, ': %d boards' % why_selected[arg]
65 print ('Total boards to build for each commit: %d\n' %
68 def DoBuildman(options, args):
69 """The main control code for buildman
72 options: Command line options object
73 args: Command line arguments (list of strings)
78 options.git_dir = os.path.join(options.git, '.git')
80 toolchains = toolchain.Toolchains()
81 toolchains.Scan(options.list_tool_chains)
82 if options.list_tool_chains:
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()
93 if not options.branch:
94 str = 'Please use -b to specify a branch to build'
95 print col.Color(col.RED, str)
97 count = gitutil.CountCommitsInBranch(options.git_dir, options.branch)
99 str = "Branch '%s' not found or has no upstream" % options.branch
100 print col.Color(col.RED, str)
102 count += 1 # Build upstream commit also
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)
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')
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
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,
128 # 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
133 series = patchstream.GetMetaDataForList(range_expr, options.git_dir, None,
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))
141 options.jobs = max(1, (multiprocessing.cpu_count() +
142 len(selected) - 1) / len(selected))
145 options.step = len(series.commits) - 1
147 # Create a new builder with the selected options
148 output_dir = os.path.join('..', options.branch)
149 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
154 # For a dry run, just show our actions as a sanity check
156 ShowActions(series, why_selected, selected, builder, options)
158 builder.force_build = options.force_build
160 # Work out which boards to build
161 board_selected = boards.GetSelectedDict()
163 print GetActionSummary(options.summary, count, board_selected, options)
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)
173 builder.BuildBoards(series.commits, board_selected,
174 options.show_errors, options.keep_outputs)