1 # Copyright (c) 2013 The Chromium OS Authors.
3 # SPDX-License-Identifier: GPL-2.0+
12 from builder import Builder
20 """Returns a plural 's' if count is not 1"""
21 return 's' if count != 1 else ''
23 def GetActionSummary(is_summary, count, selected, options):
24 """Return a string summarising the intended action.
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),
33 str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
34 GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
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.
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,
49 builder: The builder that will be used to build the commits
50 options: Command line options object
52 col = terminal.Color()
53 print 'Dry run, so not doing much. But I would do this:'
55 print GetActionSummary(False, len(series.commits), boards_selected,
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),
63 for arg in why_selected:
65 print arg, ': %d boards' % why_selected[arg]
66 print ('Total boards to build for each commit: %d\n' %
69 def DoBuildman(options, args):
70 """The main control code for buildman
73 options: Command line options object
74 args: Command line arguments (list of strings)
79 options.git_dir = os.path.join(options.git, '.git')
81 toolchains = toolchain.Toolchains()
82 toolchains.Scan(options.list_tool_chains)
83 if options.list_tool_chains:
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()
94 if not options.branch:
95 str = 'Please use -b to specify a branch to build'
96 print col.Color(col.RED, str)
98 count = gitutil.CountCommitsInBranch(options.git_dir, options.branch)
100 str = "Branch '%s' not found or has no upstream" % options.branch
101 print col.Color(col.RED, str)
103 count += 1 # Build upstream commit also
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)
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')
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
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,
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,
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))
142 options.jobs = max(1, (multiprocessing.cpu_count() +
143 len(selected) - 1) / len(selected))
146 options.step = len(series.commits) - 1
148 gnu_make = command.Output(os.path.join(options.git,
149 'scripts/show-gnu-make')).rstrip()
151 print >> sys.stderr, 'GNU Make not found'
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
161 # For a dry run, just show our actions as a sanity check
163 ShowActions(series, why_selected, selected, builder, options)
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
170 # Work out which boards to build
171 board_selected = boards.GetSelectedDict()
173 print GetActionSummary(options.summary, count, board_selected, options)
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)
183 builder.BuildBoards(series.commits, board_selected,
184 options.show_errors, options.keep_outputs)