]> Git Repo - J-u-boot.git/blame - tools/buildman/control.py
scripts: dtc: ignore files generated generated by python
[J-u-boot.git] / tools / buildman / control.py
CommitLineData
83d290c5 1# SPDX-License-Identifier: GPL-2.0+
fc3fe1c2
SG
2# Copyright (c) 2013 The Chromium OS Authors.
3#
fc3fe1c2
SG
4
5import multiprocessing
6import os
883a321a 7import shutil
fc3fe1c2
SG
8import sys
9
10import board
11import bsettings
12from builder import Builder
13import gitutil
14import patchstream
15import terminal
d4144e45 16from terminal import Print
fc3fe1c2 17import toolchain
99796923 18import command
73f30b9b 19import subprocess
fc3fe1c2
SG
20
21def GetPlural(count):
22 """Returns a plural 's' if count is not 1"""
23 return 's' if count != 1 else ''
24
fea5858e 25def GetActionSummary(is_summary, commits, selected, options):
fc3fe1c2
SG
26 """Return a string summarising the intended action.
27
28 Returns:
29 Summary string.
30 """
fea5858e
SG
31 if commits:
32 count = len(commits)
c05aa036 33 count = (count + options.step - 1) // options.step
fea5858e
SG
34 commit_str = '%d commit%s' % (count, GetPlural(count))
35 else:
36 commit_str = 'current source'
37 str = '%s %s for %d boards' % (
38 'Summary of' if is_summary else 'Building', commit_str,
fc3fe1c2
SG
39 len(selected))
40 str += ' (%d thread%s, %d job%s per thread)' % (options.threads,
41 GetPlural(options.threads), options.jobs, GetPlural(options.jobs))
42 return str
43
0689036a
SG
44def ShowActions(series, why_selected, boards_selected, builder, options,
45 board_warnings):
fc3fe1c2
SG
46 """Display a list of actions that we would take, if not a dry run.
47
48 Args:
49 series: Series object
50 why_selected: Dictionary where each key is a buildman argument
8d7523c5
SG
51 provided by the user, and the value is the list of boards
52 brought in by that argument. For example, 'arm' might bring
53 in 400 boards, so in this case the key would be 'arm' and
fc3fe1c2
SG
54 the value would be a list of board names.
55 boards_selected: Dict of selected boards, key is target name,
56 value is Board object
57 builder: The builder that will be used to build the commits
58 options: Command line options object
0689036a 59 board_warnings: List of warnings obtained from board selected
fc3fe1c2
SG
60 """
61 col = terminal.Color()
c05aa036
SG
62 print('Dry run, so not doing much. But I would do this:')
63 print()
fea5858e
SG
64 if series:
65 commits = series.commits
66 else:
67 commits = None
c05aa036
SG
68 print(GetActionSummary(False, commits, boards_selected,
69 options))
70 print('Build directory: %s' % builder.base_dir)
fea5858e
SG
71 if commits:
72 for upto in range(0, len(series.commits), options.step):
73 commit = series.commits[upto]
c05aa036
SG
74 print(' ', col.Color(col.YELLOW, commit.hash[:8], bright=False), end=' ')
75 print(commit.subject)
76 print()
fc3fe1c2
SG
77 for arg in why_selected:
78 if arg != 'all':
c05aa036 79 print(arg, ': %d boards' % len(why_selected[arg]))
8d7523c5 80 if options.verbose:
c05aa036
SG
81 print(' %s' % ' '.join(why_selected[arg]))
82 print(('Total boards to build for each commit: %d\n' %
83 len(why_selected['all'])))
0689036a
SG
84 if board_warnings:
85 for warning in board_warnings:
c05aa036 86 print(col.Color(col.YELLOW, warning))
fc3fe1c2 87
409fc029
LW
88def CheckOutputDir(output_dir):
89 """Make sure that the output directory is not within the current directory
90
91 If we try to use an output directory which is within the current directory
92 (which is assumed to hold the U-Boot source) we may end up deleting the
93 U-Boot source code. Detect this and print an error in this case.
94
95 Args:
96 output_dir: Output directory path to check
97 """
98 path = os.path.realpath(output_dir)
99 cwd_path = os.path.realpath('.')
100 while True:
101 if os.path.realpath(path) == cwd_path:
58804b8c 102 Print("Cannot use output directory '%s' since it is within the current directory '%s'" %
409fc029
LW
103 (path, cwd_path))
104 sys.exit(1)
105 parent = os.path.dirname(path)
106 if parent == path:
107 break
108 path = parent
109
883a321a
SG
110def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
111 clean_dir=False):
fc3fe1c2
SG
112 """The main control code for buildman
113
114 Args:
115 options: Command line options object
116 args: Command line arguments (list of strings)
d4144e45
SG
117 toolchains: Toolchains to use - this should be a Toolchains()
118 object. If None, then it will be created and scanned
119 make_func: Make function to use for the builder. This is called
120 to execute 'make'. If this is None, the normal function
121 will be used, which calls the 'make' tool with suitable
122 arguments. This setting is useful for tests.
823e60b6
SG
123 board: Boards() object to use, containing a list of available
124 boards. If this is None it will be created and scanned.
fc3fe1c2 125 """
883a321a
SG
126 global builder
127
48ba5856
SG
128 if options.full_help:
129 pager = os.getenv('PAGER')
130 if not pager:
131 pager = 'more'
2bdeade0
SG
132 fname = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
133 'README')
48ba5856
SG
134 command.Run(pager, fname)
135 return 0
136
fc3fe1c2 137 gitutil.Setup()
713bea38 138 col = terminal.Color()
fc3fe1c2 139
fc3fe1c2
SG
140 options.git_dir = os.path.join(options.git, '.git')
141
7e92e46e
SG
142 no_toolchains = toolchains is None
143 if no_toolchains:
00beb248 144 toolchains = toolchain.Toolchains(options.override_toolchain)
fc3fe1c2 145
827e37b5
SG
146 if options.fetch_arch:
147 if options.fetch_arch == 'list':
148 sorted_list = toolchains.ListArchs()
c05aa036
SG
149 print(col.Color(col.BLUE, 'Available architectures: %s\n' %
150 ' '.join(sorted_list)))
827e37b5
SG
151 return 0
152 else:
153 fetch_arch = options.fetch_arch
154 if fetch_arch == 'all':
155 fetch_arch = ','.join(toolchains.ListArchs())
c05aa036
SG
156 print(col.Color(col.CYAN, '\nDownloading toolchains: %s' %
157 fetch_arch))
827e37b5 158 for arch in fetch_arch.split(','):
c05aa036 159 print()
827e37b5
SG
160 ret = toolchains.FetchAndInstall(arch)
161 if ret:
162 return ret
163 return 0
164
7e92e46e
SG
165 if no_toolchains:
166 toolchains.GetSettings()
40232c91 167 toolchains.Scan(options.list_tool_chains and options.verbose)
7e92e46e
SG
168 if options.list_tool_chains:
169 toolchains.List()
c05aa036 170 print()
7e92e46e
SG
171 return 0
172
fc3fe1c2
SG
173 # Work out how many commits to build. We want to build everything on the
174 # branch. We also build the upstream commit as a control so we can see
175 # problems introduced by the first commit on the branch.
fc3fe1c2 176 count = options.count
5abab20d 177 has_range = options.branch and '..' in options.branch
fc3fe1c2
SG
178 if count == -1:
179 if not options.branch:
fea5858e
SG
180 count = 1
181 else:
5abab20d
SG
182 if has_range:
183 count, msg = gitutil.CountCommitsInRange(options.git_dir,
184 options.branch)
185 else:
186 count, msg = gitutil.CountCommitsInBranch(options.git_dir,
187 options.branch)
fea5858e 188 if count is None:
2a9e2c6a 189 sys.exit(col.Color(col.RED, msg))
5abab20d
SG
190 elif count == 0:
191 sys.exit(col.Color(col.RED, "Range '%s' has no commits" %
192 options.branch))
2a9e2c6a 193 if msg:
c05aa036 194 print(col.Color(col.YELLOW, msg))
fea5858e 195 count += 1 # Build upstream commit also
fc3fe1c2
SG
196
197 if not count:
198 str = ("No commits found to process in branch '%s': "
199 "set branch's upstream or use -c flag" % options.branch)
31e2141d 200 sys.exit(col.Color(col.RED, str))
fc3fe1c2
SG
201
202 # Work out what subset of the boards we are building
823e60b6 203 if not boards:
bc750bca
BM
204 board_file = os.path.join(options.output_dir, 'boards.cfg')
205 genboardscfg = os.path.join(options.git, 'tools/genboardscfg.py')
206 status = subprocess.call([genboardscfg, '-o', board_file])
823e60b6 207 if status != 0:
bc750bca 208 sys.exit("Failed to generate boards.cfg")
823e60b6
SG
209
210 boards = board.Boards()
bc750bca 211 boards.ReadBoards(board_file)
3cf4ae6f
SG
212
213 exclude = []
214 if options.exclude:
215 for arg in options.exclude:
216 exclude += arg.split(',')
217
0689036a
SG
218
219 if options.boards:
220 requested_boards = []
221 for b in options.boards:
222 requested_boards += b.split(',')
223 else:
224 requested_boards = None
225 why_selected, board_warnings = boards.SelectBoards(args, exclude,
226 requested_boards)
fc3fe1c2
SG
227 selected = boards.GetSelected()
228 if not len(selected):
31e2141d 229 sys.exit(col.Color(col.RED, 'No matching boards found'))
fc3fe1c2
SG
230
231 # Read the metadata from the commits. First look at the upstream commit,
232 # then the ones in the branch. We would like to do something like
233 # upstream/master~..branch but that isn't possible if upstream/master is
234 # a merge commit (it will list all the commits that form part of the
235 # merge)
950a2313
SG
236 # Conflicting tags are not a problem for buildman, since it does not use
237 # them. For example, Series-version is not useful for buildman. On the
238 # other hand conflicting tags will cause an error. So allow later tags
239 # to overwrite earlier ones by setting allow_overwrite=True
fea5858e 240 if options.branch:
3b74ba5f 241 if count == -1:
5abab20d
SG
242 if has_range:
243 range_expr = options.branch
244 else:
245 range_expr = gitutil.GetRangeInBranch(options.git_dir,
246 options.branch)
3b74ba5f
SG
247 upstream_commit = gitutil.GetUpstream(options.git_dir,
248 options.branch)
249 series = patchstream.GetMetaDataForList(upstream_commit,
950a2313 250 options.git_dir, 1, series=None, allow_overwrite=True)
3b74ba5f 251
3b74ba5f 252 series = patchstream.GetMetaDataForList(range_expr,
950a2313 253 options.git_dir, None, series, allow_overwrite=True)
3b74ba5f
SG
254 else:
255 # Honour the count
256 series = patchstream.GetMetaDataForList(options.branch,
950a2313 257 options.git_dir, count, series=None, allow_overwrite=True)
fea5858e
SG
258 else:
259 series = None
8d7523c5
SG
260 if not options.dry_run:
261 options.verbose = True
262 if not options.summary:
263 options.show_errors = True
fc3fe1c2
SG
264
265 # By default we have one thread per CPU. But if there are not enough jobs
266 # we can have fewer threads and use a high '-j' value for make.
267 if not options.threads:
268 options.threads = min(multiprocessing.cpu_count(), len(selected))
269 if not options.jobs:
270 options.jobs = max(1, (multiprocessing.cpu_count() +
c05aa036 271 len(selected) - 1) // len(selected))
fc3fe1c2
SG
272
273 if not options.step:
274 options.step = len(series.commits) - 1
275
99796923 276 gnu_make = command.Output(os.path.join(options.git,
785f1548 277 'scripts/show-gnu-make'), raise_on_error=False).rstrip()
99796923 278 if not gnu_make:
31e2141d 279 sys.exit('GNU Make not found')
99796923 280
05c96b18
SG
281 # Create a new builder with the selected options.
282 output_dir = options.output_dir
fea5858e 283 if options.branch:
f7582ce8 284 dirname = options.branch.replace('/', '_')
5971ab5c
SG
285 # As a special case allow the board directory to be placed in the
286 # output directory itself rather than any subdirectory.
287 if not options.no_subdirs:
288 output_dir = os.path.join(options.output_dir, dirname)
409fc029
LW
289 if clean_dir and os.path.exists(output_dir):
290 shutil.rmtree(output_dir)
291 CheckOutputDir(output_dir)
fc3fe1c2 292 builder = Builder(toolchains, output_dir, options.git_dir,
99796923 293 options.threads, options.jobs, gnu_make=gnu_make, checkout=True,
5971ab5c 294 show_unknown=options.show_unknown, step=options.step,
d2ce658d 295 no_subdirs=options.no_subdirs, full_path=options.full_path,
f79f1e0c
SW
296 verbose_build=options.verbose_build,
297 incremental=options.incremental,
b50113f3 298 per_board_out_dir=options.per_board_out_dir,
b464f8e7 299 config_only=options.config_only,
2371d1bc
DS
300 squash_config_y=not options.preserve_config_y,
301 warnings_as_errors=options.warnings_as_errors)
fc3fe1c2 302 builder.force_config_on_failure = not options.quick
d4144e45
SG
303 if make_func:
304 builder.do_make = make_func
fc3fe1c2
SG
305
306 # For a dry run, just show our actions as a sanity check
307 if options.dry_run:
0689036a
SG
308 ShowActions(series, why_selected, selected, builder, options,
309 board_warnings)
fc3fe1c2
SG
310 else:
311 builder.force_build = options.force_build
4266dc28 312 builder.force_build_failures = options.force_build_failures
97e91526 313 builder.force_reconfig = options.force_reconfig
189a4968 314 builder.in_tree = options.in_tree
fc3fe1c2
SG
315
316 # Work out which boards to build
317 board_selected = boards.GetSelectedDict()
318
fea5858e
SG
319 if series:
320 commits = series.commits
883a321a
SG
321 # Number the commits for test purposes
322 for commit in range(len(commits)):
323 commits[commit].sequence = commit
fea5858e
SG
324 else:
325 commits = None
326
d4144e45
SG
327 Print(GetActionSummary(options.summary, commits, board_selected,
328 options))
fc3fe1c2 329
7798e228
SG
330 # We can't show function sizes without board details at present
331 if options.show_bloat:
332 options.show_detail = True
b2ea7ab2 333 builder.SetDisplayOptions(options.show_errors, options.show_sizes,
ed966657 334 options.show_detail, options.show_bloat,
843312dc 335 options.list_error_boards,
48ae4124
AK
336 options.show_config,
337 options.show_environment)
fc3fe1c2 338 if options.summary:
b2ea7ab2 339 builder.ShowSummary(commits, board_selected)
fc3fe1c2 340 else:
2c3deb97 341 fail, warned = builder.BuildBoards(commits, board_selected,
e5a0e5d8 342 options.keep_outputs, options.verbose)
2c3deb97
SG
343 if fail:
344 return 128
345 elif warned:
346 return 129
347 return 0
This page took 0.378692 seconds and 4 git commands to generate.