blob: 6ca79c2c0f9451e2cc029f2660c2b5532eddaebe [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassc05694f2013-04-03 11:07:16 +00002# Copyright (c) 2012 The Chromium OS Authors.
3#
Simon Glassc05694f2013-04-03 11:07:16 +00004
Simon Glasscc246fb2013-09-23 17:35:17 -06005import re
Simon Glassc05694f2013-04-03 11:07:16 +00006import glob
Simon Glassc78ed662019-10-31 07:42:53 -06007from html.parser import HTMLParser
Simon Glassc05694f2013-04-03 11:07:16 +00008import os
Simon Glass7e803e12014-12-01 17:34:06 -07009import sys
10import tempfile
Simon Glassc78ed662019-10-31 07:42:53 -060011import urllib.request, urllib.error, urllib.parse
Simon Glassc05694f2013-04-03 11:07:16 +000012
Simon Glassf0d9c102020-04-17 18:09:02 -060013from buildman import bsettings
Simon Glass131444f2023-02-23 18:18:04 -070014from u_boot_pylib import command
15from u_boot_pylib import terminal
16from u_boot_pylib import tools
Simon Glassc05694f2013-04-03 11:07:16 +000017
Simon Glassf5902732016-03-12 18:50:32 -070018(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH,
Simon Glassc78ed662019-10-31 07:42:53 -060019 PRIORITY_CALC) = list(range(4))
Simon Glass7b971292016-03-06 19:45:37 -070020
Simon Glass48ac42e2019-12-05 15:59:14 -070021(VAR_CROSS_COMPILE, VAR_PATH, VAR_ARCH, VAR_MAKE_ARGS) = range(4)
22
Simon Glass7e803e12014-12-01 17:34:06 -070023# Simple class to collect links from a page
24class MyHTMLParser(HTMLParser):
25 def __init__(self, arch):
26 """Create a new parser
27
28 After the parser runs, self.links will be set to a list of the links
29 to .xz archives found in the page, and self.arch_link will be set to
30 the one for the given architecture (or None if not found).
31
32 Args:
33 arch: Architecture to search for
34 """
35 HTMLParser.__init__(self)
36 self.arch_link = None
37 self.links = []
Daniel Schwierzeck40261f92018-05-10 07:15:53 -040038 self.re_arch = re.compile('[-_]%s-' % arch)
Simon Glass7e803e12014-12-01 17:34:06 -070039
40 def handle_starttag(self, tag, attrs):
41 if tag == 'a':
42 for tag, value in attrs:
43 if tag == 'href':
44 if value and value.endswith('.xz'):
45 self.links.append(value)
Daniel Schwierzeck40261f92018-05-10 07:15:53 -040046 if self.re_arch.search(value):
Simon Glass7e803e12014-12-01 17:34:06 -070047 self.arch_link = value
48
49
Simon Glassc05694f2013-04-03 11:07:16 +000050class Toolchain:
51 """A single toolchain
52
53 Public members:
54 gcc: Full path to C compiler
55 path: Directory path containing C compiler
56 cross: Cross compile string, e.g. 'arm-linux-'
57 arch: Architecture of toolchain as determined from the first
58 component of the filename. E.g. arm-linux-gcc becomes arm
Simon Glass7b971292016-03-06 19:45:37 -070059 priority: Toolchain priority (0=highest, 20=lowest)
Simon Glassf77ca5b2019-01-07 16:44:20 -070060 override_toolchain: Toolchain to use for sandbox, overriding the normal
61 one
Simon Glassc05694f2013-04-03 11:07:16 +000062 """
Simon Glassc6cdd3e2016-03-06 19:45:38 -070063 def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC,
Simon Glassf77ca5b2019-01-07 16:44:20 -070064 arch=None, override_toolchain=None):
Simon Glassc05694f2013-04-03 11:07:16 +000065 """Create a new toolchain object.
66
67 Args:
68 fname: Filename of the gcc component
69 test: True to run the toolchain to test it
Simon Glassd6ece322016-03-06 19:45:35 -070070 verbose: True to print out the information
Simon Glass7b971292016-03-06 19:45:37 -070071 priority: Priority to use for this toolchain, or PRIORITY_CALC to
72 calculate it
Simon Glassc05694f2013-04-03 11:07:16 +000073 """
74 self.gcc = fname
75 self.path = os.path.dirname(fname)
Simon Glassf77ca5b2019-01-07 16:44:20 -070076 self.override_toolchain = override_toolchain
Simon Glass28ed0062014-12-01 17:33:58 -070077
78 # Find the CROSS_COMPILE prefix to use for U-Boot. For example,
79 # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'.
80 basename = os.path.basename(fname)
81 pos = basename.rfind('-')
82 self.cross = basename[:pos + 1] if pos != -1 else ''
83
84 # The architecture is the first part of the name
Simon Glassc05694f2013-04-03 11:07:16 +000085 pos = self.cross.find('-')
Simon Glassc6cdd3e2016-03-06 19:45:38 -070086 if arch:
87 self.arch = arch
88 else:
89 self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
Simon Glassf77ca5b2019-01-07 16:44:20 -070090 if self.arch == 'sandbox' and override_toolchain:
91 self.gcc = override_toolchain
Simon Glassc05694f2013-04-03 11:07:16 +000092
Tom Rini3f6f9b22024-07-05 14:34:07 -060093 env = self.MakeEnvironment(False)
Simon Glassc05694f2013-04-03 11:07:16 +000094
95 # As a basic sanity check, run the C compiler with --version
96 cmd = [fname, '--version']
Simon Glass7b971292016-03-06 19:45:37 -070097 if priority == PRIORITY_CALC:
98 self.priority = self.GetPriority(fname)
99 else:
100 self.priority = priority
Simon Glassc05694f2013-04-03 11:07:16 +0000101 if test:
Simon Glass840be732022-01-29 14:14:05 -0700102 result = command.run_pipe([cmd], capture=True, env=env,
Stephen Warren288d7672013-10-09 14:28:09 -0600103 raise_on_error=False)
Simon Glassc05694f2013-04-03 11:07:16 +0000104 self.ok = result.return_code == 0
105 if verbose:
Simon Glassc78ed662019-10-31 07:42:53 -0600106 print('Tool chain test: ', end=' ')
Simon Glassc05694f2013-04-03 11:07:16 +0000107 if self.ok:
Simon Glassc78ed662019-10-31 07:42:53 -0600108 print("OK, arch='%s', priority %d" % (self.arch,
109 self.priority))
Simon Glassc05694f2013-04-03 11:07:16 +0000110 else:
Simon Glassc78ed662019-10-31 07:42:53 -0600111 print('BAD')
112 print('Command: ', cmd)
113 print(result.stdout)
114 print(result.stderr)
Simon Glassc05694f2013-04-03 11:07:16 +0000115 else:
116 self.ok = True
Simon Glassc05694f2013-04-03 11:07:16 +0000117
118 def GetPriority(self, fname):
119 """Return the priority of the toolchain.
120
121 Toolchains are ranked according to their suitability by their
122 filename prefix.
123
124 Args:
125 fname: Filename of toolchain
126 Returns:
Simon Glass7b971292016-03-06 19:45:37 -0700127 Priority of toolchain, PRIORITY_CALC=highest, 20=lowest.
Simon Glassc05694f2013-04-03 11:07:16 +0000128 """
Masahiro Yamadafa25e1f2014-07-07 09:47:45 +0900129 priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
Tom Rini6abc9c82017-04-14 19:47:50 -0400130 '-none-linux-gnueabi', '-none-linux-gnueabihf', '-uclinux',
131 '-none-eabi', '-gentoo-linux-gnu', '-linux-gnueabi',
132 '-linux-gnueabihf', '-le-linux', '-uclinux']
Simon Glassc05694f2013-04-03 11:07:16 +0000133 for prio in range(len(priority_list)):
134 if priority_list[prio] in fname:
Simon Glass7b971292016-03-06 19:45:37 -0700135 return PRIORITY_CALC + prio
136 return PRIORITY_CALC + prio
Simon Glassc05694f2013-04-03 11:07:16 +0000137
York Sunfb197a82016-10-04 14:33:51 -0700138 def GetWrapper(self, show_warning=True):
139 """Get toolchain wrapper from the setting file.
140 """
Simon Glass45572632019-01-07 16:44:24 -0700141 value = ''
Simon Glass06b83a52023-07-19 17:49:05 -0600142 for name, value in bsettings.get_items('toolchain-wrapper'):
York Sunfb197a82016-10-04 14:33:51 -0700143 if not value:
Simon Glassc78ed662019-10-31 07:42:53 -0600144 print("Warning: Wrapper not found")
York Sunfb197a82016-10-04 14:33:51 -0700145 if value:
146 value = value + ' '
147
148 return value
149
Simon Glass48ac42e2019-12-05 15:59:14 -0700150 def GetEnvArgs(self, which):
151 """Get an environment variable/args value based on the the toolchain
152
153 Args:
154 which: VAR_... value to get
155
156 Returns:
157 Value of that environment variable or arguments
158 """
Simon Glass48ac42e2019-12-05 15:59:14 -0700159 if which == VAR_CROSS_COMPILE:
Simon Glass09753b32023-03-10 12:48:51 -0800160 wrapper = self.GetWrapper()
161 base = '' if self.arch == 'sandbox' else self.path
162 return wrapper + os.path.join(base, self.cross)
Simon Glass48ac42e2019-12-05 15:59:14 -0700163 elif which == VAR_PATH:
164 return self.path
165 elif which == VAR_ARCH:
166 return self.arch
167 elif which == VAR_MAKE_ARGS:
168 args = self.MakeArgs()
169 if args:
170 return ' '.join(args)
171 return ''
172 else:
173 raise ValueError('Unknown arg to GetEnvArgs (%d)' % which)
174
Simon Glass038a3dd2024-08-15 13:57:44 -0600175 def MakeEnvironment(self, full_path, env=None):
Simon Glassc05694f2013-04-03 11:07:16 +0000176 """Returns an environment for using the toolchain.
177
Simon Glass58225462024-06-23 11:56:19 -0600178 This takes the current environment and adds CROSS_COMPILE so that
Daniel Schwierzeckc9e5f0c2017-06-08 03:07:08 +0200179 the tool chain will operate correctly. This also disables localized
Simon Glass58225462024-06-23 11:56:19 -0600180 output and possibly Unicode encoded output of all build tools by
Simon Glass038a3dd2024-08-15 13:57:44 -0600181 adding LC_ALL=C. For the case where full_path is False, it prepends
182 the toolchain to PATH
Simon Glassd48a46c2014-12-01 17:34:00 -0700183
Simon Glass93008e22021-04-11 16:27:28 +1200184 Note that os.environb is used to obtain the environment, since in some
185 cases the environment many contain non-ASCII characters and we see
186 errors like:
187
188 UnicodeEncodeError: 'utf-8' codec can't encode characters in position
189 569-570: surrogates not allowed
190
Simon Glass038a3dd2024-08-15 13:57:44 -0600191 When running inside a Python venv, care is taken not to put the
192 toolchain path before the venv path, so that builds initiated by
193 buildman will still respect the venv.
194
Simon Glassd48a46c2014-12-01 17:34:00 -0700195 Args:
Tom Rini3f6f9b22024-07-05 14:34:07 -0600196 full_path: Return the full path in CROSS_COMPILE and don't set
197 PATH
Simon Glass038a3dd2024-08-15 13:57:44 -0600198 env (dict of bytes): Original environment, used for testing
Simon Glassf77ca5b2019-01-07 16:44:20 -0700199 Returns:
Simon Glass93008e22021-04-11 16:27:28 +1200200 Dict containing the (bytes) environment to use. This is based on the
Tom Rini3f6f9b22024-07-05 14:34:07 -0600201 current environment, with changes as needed to CROSS_COMPILE, PATH
202 and LC_ALL.
Simon Glassc05694f2013-04-03 11:07:16 +0000203 """
Simon Glass038a3dd2024-08-15 13:57:44 -0600204 env = dict(env or os.environb)
205
York Sunfb197a82016-10-04 14:33:51 -0700206 wrapper = self.GetWrapper()
207
Simon Glassf77ca5b2019-01-07 16:44:20 -0700208 if self.override_toolchain:
209 # We'll use MakeArgs() to provide this
210 pass
Tom Rini3f6f9b22024-07-05 14:34:07 -0600211 elif full_path:
Simon Glass80025522022-01-29 14:14:04 -0700212 env[b'CROSS_COMPILE'] = tools.to_bytes(
Simon Glass93008e22021-04-11 16:27:28 +1200213 wrapper + os.path.join(self.path, self.cross))
Tom Rini3f6f9b22024-07-05 14:34:07 -0600214 else:
215 env[b'CROSS_COMPILE'] = tools.to_bytes(wrapper + self.cross)
Simon Glass038a3dd2024-08-15 13:57:44 -0600216
217 # Detect a Python virtualenv and avoid defeating it
218 if sys.prefix != sys.base_prefix:
219 paths = env[b'PATH'].split(b':')
220 new_paths = []
221 to_insert = tools.to_bytes(self.path)
222 insert_after = tools.to_bytes(sys.prefix)
223 for path in paths:
224 new_paths.append(path)
225 if to_insert and path.startswith(insert_after):
226 new_paths.append(to_insert)
227 to_insert = None
228 if to_insert:
229 new_paths.append(to_insert)
230 env[b'PATH'] = b':'.join(new_paths)
231 else:
232 env[b'PATH'] = tools.to_bytes(self.path) + b':' + env[b'PATH']
Simon Glassd48a46c2014-12-01 17:34:00 -0700233
Simon Glass93008e22021-04-11 16:27:28 +1200234 env[b'LC_ALL'] = b'C'
Daniel Schwierzeckc9e5f0c2017-06-08 03:07:08 +0200235
Simon Glassc05694f2013-04-03 11:07:16 +0000236 return env
237
Simon Glassf77ca5b2019-01-07 16:44:20 -0700238 def MakeArgs(self):
239 """Create the 'make' arguments for a toolchain
240
241 This is only used when the toolchain is being overridden. Since the
242 U-Boot Makefile sets CC and HOSTCC explicitly we cannot rely on the
243 environment (and MakeEnvironment()) to override these values. This
244 function returns the arguments to accomplish this.
245
246 Returns:
247 List of arguments to pass to 'make'
248 """
249 if self.override_toolchain:
250 return ['HOSTCC=%s' % self.override_toolchain,
251 'CC=%s' % self.override_toolchain]
252 return []
253
Simon Glassc05694f2013-04-03 11:07:16 +0000254
255class Toolchains:
256 """Manage a list of toolchains for building U-Boot
257
258 We select one toolchain for each architecture type
259
260 Public members:
261 toolchains: Dict of Toolchain objects, keyed by architecture name
Simon Glassf5902732016-03-12 18:50:32 -0700262 prefixes: Dict of prefixes to check, keyed by architecture. This can
263 be a full path and toolchain prefix, for example
264 {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of
265 something on the search path, for example
266 {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported.
Simon Glassc05694f2013-04-03 11:07:16 +0000267 paths: List of paths to check for toolchains (may contain wildcards)
268 """
269
Simon Glassf77ca5b2019-01-07 16:44:20 -0700270 def __init__(self, override_toolchain=None):
Simon Glassc05694f2013-04-03 11:07:16 +0000271 self.toolchains = {}
Simon Glassf5902732016-03-12 18:50:32 -0700272 self.prefixes = {}
Simon Glassc05694f2013-04-03 11:07:16 +0000273 self.paths = []
Simon Glassf77ca5b2019-01-07 16:44:20 -0700274 self.override_toolchain = override_toolchain
Simon Glass06b83a52023-07-19 17:49:05 -0600275 self._make_flags = dict(bsettings.get_items('make-flags'))
Simon Glassed098bb2014-09-05 19:00:13 -0600276
Simon Glassf38a6422016-07-27 20:33:01 -0600277 def GetPathList(self, show_warning=True):
Simon Glass7e803e12014-12-01 17:34:06 -0700278 """Get a list of available toolchain paths
279
Simon Glassf38a6422016-07-27 20:33:01 -0600280 Args:
281 show_warning: True to show a warning if there are no tool chains.
282
Simon Glass7e803e12014-12-01 17:34:06 -0700283 Returns:
284 List of strings, each a path to a toolchain mentioned in the
285 [toolchain] section of the settings file.
286 """
Simon Glass06b83a52023-07-19 17:49:05 -0600287 toolchains = bsettings.get_items('toolchain')
Simon Glassf38a6422016-07-27 20:33:01 -0600288 if show_warning and not toolchains:
Simon Glassc78ed662019-10-31 07:42:53 -0600289 print(("Warning: No tool chains. Please run 'buildman "
Simon Glass9f1ba0f2016-07-27 20:33:02 -0600290 "--fetch-arch all' to download all available toolchains, or "
291 "add a [toolchain] section to your buildman config file "
Heinrich Schuchardtf04e8002023-01-19 16:22:10 +0100292 "%s. See buildman.rst for details" %
Simon Glassc78ed662019-10-31 07:42:53 -0600293 bsettings.config_fname))
Simon Glasscc246fb2013-09-23 17:35:17 -0600294
Simon Glass7e803e12014-12-01 17:34:06 -0700295 paths = []
Simon Glasscc246fb2013-09-23 17:35:17 -0600296 for name, value in toolchains:
Simon Glassc05694f2013-04-03 11:07:16 +0000297 if '*' in value:
Simon Glass7e803e12014-12-01 17:34:06 -0700298 paths += glob.glob(value)
Simon Glassc05694f2013-04-03 11:07:16 +0000299 else:
Simon Glass7e803e12014-12-01 17:34:06 -0700300 paths.append(value)
301 return paths
302
Simon Glassf38a6422016-07-27 20:33:01 -0600303 def GetSettings(self, show_warning=True):
304 """Get toolchain settings from the settings file.
305
306 Args:
307 show_warning: True to show a warning if there are no tool chains.
308 """
Simon Glass06b83a52023-07-19 17:49:05 -0600309 self.prefixes = bsettings.get_items('toolchain-prefix')
Simon Glassf38a6422016-07-27 20:33:01 -0600310 self.paths += self.GetPathList(show_warning)
Simon Glassc05694f2013-04-03 11:07:16 +0000311
Simon Glassc6cdd3e2016-03-06 19:45:38 -0700312 def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
313 arch=None):
Simon Glassc05694f2013-04-03 11:07:16 +0000314 """Add a toolchain to our list
315
316 We select the given toolchain as our preferred one for its
317 architecture if it is a higher priority than the others.
318
319 Args:
320 fname: Filename of toolchain's gcc driver
321 test: True to run the toolchain to test it
Simon Glass7b971292016-03-06 19:45:37 -0700322 priority: Priority to use for this toolchain
Simon Glassc6cdd3e2016-03-06 19:45:38 -0700323 arch: Toolchain architecture, or None if not known
Simon Glassc05694f2013-04-03 11:07:16 +0000324 """
Simon Glassf77ca5b2019-01-07 16:44:20 -0700325 toolchain = Toolchain(fname, test, verbose, priority, arch,
326 self.override_toolchain)
Simon Glassc05694f2013-04-03 11:07:16 +0000327 add_it = toolchain.ok
328 if toolchain.arch in self.toolchains:
329 add_it = (toolchain.priority <
330 self.toolchains[toolchain.arch].priority)
331 if add_it:
332 self.toolchains[toolchain.arch] = toolchain
Simon Glass7b971292016-03-06 19:45:37 -0700333 elif verbose:
Simon Glassc78ed662019-10-31 07:42:53 -0600334 print(("Toolchain '%s' at priority %d will be ignored because "
Simon Glass7b971292016-03-06 19:45:37 -0700335 "another toolchain for arch '%s' has priority %d" %
336 (toolchain.gcc, toolchain.priority, toolchain.arch,
Simon Glassc78ed662019-10-31 07:42:53 -0600337 self.toolchains[toolchain.arch].priority)))
Simon Glassc05694f2013-04-03 11:07:16 +0000338
Simon Glass7e803e12014-12-01 17:34:06 -0700339 def ScanPath(self, path, verbose):
340 """Scan a path for a valid toolchain
341
342 Args:
343 path: Path to scan
344 verbose: True to print out progress information
345 Returns:
346 Filename of C compiler if found, else None
347 """
Albert ARIBAUDd4f22cf2015-02-01 00:12:44 +0100348 fnames = []
Simon Glass7e803e12014-12-01 17:34:06 -0700349 for subdir in ['.', 'bin', 'usr/bin']:
350 dirname = os.path.join(path, subdir)
Simon Glassc78ed662019-10-31 07:42:53 -0600351 if verbose: print(" - looking in '%s'" % dirname)
Simon Glass7e803e12014-12-01 17:34:06 -0700352 for fname in glob.glob(dirname + '/*gcc'):
Simon Glassc78ed662019-10-31 07:42:53 -0600353 if verbose: print(" - found '%s'" % fname)
Albert ARIBAUDd4f22cf2015-02-01 00:12:44 +0100354 fnames.append(fname)
355 return fnames
Simon Glass7e803e12014-12-01 17:34:06 -0700356
Simon Glassf5902732016-03-12 18:50:32 -0700357 def ScanPathEnv(self, fname):
358 """Scan the PATH environment variable for a given filename.
359
360 Args:
361 fname: Filename to scan for
362 Returns:
363 List of matching pathanames, or [] if none
364 """
365 pathname_list = []
366 for path in os.environ["PATH"].split(os.pathsep):
367 path = path.strip('"')
368 pathname = os.path.join(path, fname)
369 if os.path.exists(pathname):
370 pathname_list.append(pathname)
371 return pathname_list
Simon Glass7e803e12014-12-01 17:34:06 -0700372
Simon Glassc05694f2013-04-03 11:07:16 +0000373 def Scan(self, verbose):
374 """Scan for available toolchains and select the best for each arch.
375
376 We look for all the toolchains we can file, figure out the
377 architecture for each, and whether it works. Then we select the
378 highest priority toolchain for each arch.
379
380 Args:
381 verbose: True to print out progress information
382 """
Simon Glassc78ed662019-10-31 07:42:53 -0600383 if verbose: print('Scanning for tool chains')
Simon Glassf5902732016-03-12 18:50:32 -0700384 for name, value in self.prefixes:
Simon Glassc78ed662019-10-31 07:42:53 -0600385 if verbose: print(" - scanning prefix '%s'" % value)
Simon Glassf5902732016-03-12 18:50:32 -0700386 if os.path.exists(value):
387 self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name)
388 continue
389 fname = value + 'gcc'
390 if os.path.exists(fname):
391 self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name)
392 continue
393 fname_list = self.ScanPathEnv(fname)
394 for f in fname_list:
395 self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name)
396 if not fname_list:
Simon Glassc78ed662019-10-31 07:42:53 -0600397 raise ValueError("No tool chain found for prefix '%s'" %
Simon Glassf5902732016-03-12 18:50:32 -0700398 value)
Simon Glassc05694f2013-04-03 11:07:16 +0000399 for path in self.paths:
Simon Glassc78ed662019-10-31 07:42:53 -0600400 if verbose: print(" - scanning path '%s'" % path)
Albert ARIBAUDd4f22cf2015-02-01 00:12:44 +0100401 fnames = self.ScanPath(path, verbose)
402 for fname in fnames:
Simon Glass7e803e12014-12-01 17:34:06 -0700403 self.Add(fname, True, verbose)
Simon Glassc05694f2013-04-03 11:07:16 +0000404
405 def List(self):
406 """List out the selected toolchains for each architecture"""
Simon Glass9f1ba0f2016-07-27 20:33:02 -0600407 col = terminal.Color()
Simon Glassf45d3742022-01-29 14:14:17 -0700408 print(col.build(col.BLUE, 'List of available toolchains (%d):' %
Simon Glassc78ed662019-10-31 07:42:53 -0600409 len(self.toolchains)))
Simon Glassc05694f2013-04-03 11:07:16 +0000410 if len(self.toolchains):
Simon Glassc78ed662019-10-31 07:42:53 -0600411 for key, value in sorted(self.toolchains.items()):
412 print('%-10s: %s' % (key, value.gcc))
Simon Glassc05694f2013-04-03 11:07:16 +0000413 else:
Simon Glassc78ed662019-10-31 07:42:53 -0600414 print('None')
Simon Glassc05694f2013-04-03 11:07:16 +0000415
416 def Select(self, arch):
417 """Returns the toolchain for a given architecture
418
419 Args:
420 args: Name of architecture (e.g. 'arm', 'ppc_8xx')
421
422 returns:
423 toolchain object, or None if none found
424 """
Simon Glass06b83a52023-07-19 17:49:05 -0600425 for tag, value in bsettings.get_items('toolchain-alias'):
Simon Glassc1528c12014-12-01 17:34:05 -0700426 if arch == tag:
427 for alias in value.split():
428 if alias in self.toolchains:
429 return self.toolchains[alias]
Simon Glassc05694f2013-04-03 11:07:16 +0000430
431 if not arch in self.toolchains:
Simon Glassc78ed662019-10-31 07:42:53 -0600432 raise ValueError("No tool chain found for arch '%s'" % arch)
Simon Glassc05694f2013-04-03 11:07:16 +0000433 return self.toolchains[arch]
Simon Glasscc246fb2013-09-23 17:35:17 -0600434
435 def ResolveReferences(self, var_dict, args):
436 """Resolve variable references in a string
437
438 This converts ${blah} within the string to the value of blah.
439 This function works recursively.
440
441 Args:
442 var_dict: Dictionary containing variables and their values
443 args: String containing make arguments
444 Returns:
445 Resolved string
446
Simon Glass06b83a52023-07-19 17:49:05 -0600447 >>> bsettings.setup(None)
Simon Glasscc246fb2013-09-23 17:35:17 -0600448 >>> tcs = Toolchains()
449 >>> tcs.Add('fred', False)
450 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
451 'second' : '2nd'}
452 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
453 'this=OBLIQUE_set'
454 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
455 'this=OBLIQUE_setfi2ndrstnd'
456 """
Simon Glass53e189d2014-08-28 09:43:40 -0600457 re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
Simon Glasscc246fb2013-09-23 17:35:17 -0600458
459 while True:
460 m = re_var.search(args)
461 if not m:
462 break
463 lookup = m.group(0)[2:-1]
464 value = var_dict.get(lookup, '')
465 args = args[:m.start(0)] + value + args[m.end(0):]
466 return args
467
Simon Glass8132f982022-07-11 19:03:57 -0600468 def GetMakeArguments(self, brd):
Simon Glasscc246fb2013-09-23 17:35:17 -0600469 """Returns 'make' arguments for a given board
470
471 The flags are in a section called 'make-flags'. Flags are named
472 after the target they represent, for example snapper9260=TESTING=1
473 will pass TESTING=1 to make when building the snapper9260 board.
474
475 References to other boards can be added in the string also. For
476 example:
477
478 [make-flags]
479 at91-boards=ENABLE_AT91_TEST=1
480 snapper9260=${at91-boards} BUILD_TAG=442
481 snapper9g45=${at91-boards} BUILD_TAG=443
482
483 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
484 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
485
486 A special 'target' variable is set to the board target.
487
488 Args:
Simon Glass8132f982022-07-11 19:03:57 -0600489 brd: Board object for the board to check.
Simon Glasscc246fb2013-09-23 17:35:17 -0600490 Returns:
491 'make' flags for that board, or '' if none
492 """
Simon Glass8132f982022-07-11 19:03:57 -0600493 self._make_flags['target'] = brd.target
Simon Glasscc246fb2013-09-23 17:35:17 -0600494 arg_str = self.ResolveReferences(self._make_flags,
Simon Glass8132f982022-07-11 19:03:57 -0600495 self._make_flags.get(brd.target, ''))
Cristian Ciocaltea9da17fb2019-11-24 22:30:26 +0200496 args = re.findall("(?:\".*?\"|\S)+", arg_str)
Simon Glasscc246fb2013-09-23 17:35:17 -0600497 i = 0
498 while i < len(args):
Cristian Ciocaltea9da17fb2019-11-24 22:30:26 +0200499 args[i] = args[i].replace('"', '')
Simon Glasscc246fb2013-09-23 17:35:17 -0600500 if not args[i]:
501 del args[i]
502 else:
503 i += 1
504 return args
Simon Glass7e803e12014-12-01 17:34:06 -0700505
506 def LocateArchUrl(self, fetch_arch):
507 """Find a toolchain available online
508
509 Look in standard places for available toolchains. At present the
510 only standard place is at kernel.org.
511
512 Args:
513 arch: Architecture to look for, or 'list' for all
514 Returns:
515 If fetch_arch is 'list', a tuple:
516 Machine architecture (e.g. x86_64)
517 List of toolchains
518 else
519 URL containing this toolchain, if avaialble, else None
520 """
Simon Glass840be732022-01-29 14:14:05 -0700521 arch = command.output_one_line('uname', '-m')
Matthias Bruggerfb50a262020-01-17 10:53:37 +0100522 if arch == 'aarch64':
523 arch = 'arm64'
Simon Glass7e803e12014-12-01 17:34:06 -0700524 base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
Tom Rini3bd5ed72023-08-25 13:21:26 -0400525 versions = ['13.2.0', '12.2.0']
Simon Glass7e803e12014-12-01 17:34:06 -0700526 links = []
527 for version in versions:
528 url = '%s/%s/%s/' % (base, arch, version)
Simon Glassc78ed662019-10-31 07:42:53 -0600529 print('Checking: %s' % url)
530 response = urllib.request.urlopen(url)
Simon Glass80025522022-01-29 14:14:04 -0700531 html = tools.to_string(response.read())
Simon Glass7e803e12014-12-01 17:34:06 -0700532 parser = MyHTMLParser(fetch_arch)
533 parser.feed(html)
534 if fetch_arch == 'list':
535 links += parser.links
536 elif parser.arch_link:
537 return url + parser.arch_link
538 if fetch_arch == 'list':
539 return arch, links
540 return None
541
Simon Glass7e803e12014-12-01 17:34:06 -0700542 def Unpack(self, fname, dest):
543 """Unpack a tar file
544
545 Args:
546 fname: Filename to unpack
547 dest: Destination directory
548 Returns:
549 Directory name of the first entry in the archive, without the
550 trailing /
551 """
Simon Glass840be732022-01-29 14:14:05 -0700552 stdout = command.output('tar', 'xvfJ', fname, '-C', dest)
Trevor Woernere0557a72018-11-21 03:31:12 -0500553 dirs = stdout.splitlines()[1].split('/')[:2]
554 return '/'.join(dirs)
Simon Glass7e803e12014-12-01 17:34:06 -0700555
556 def TestSettingsHasPath(self, path):
Simon Glass43ea1282016-07-27 20:33:03 -0600557 """Check if buildman will find this toolchain
Simon Glass7e803e12014-12-01 17:34:06 -0700558
559 Returns:
560 True if the path is in settings, False if not
561 """
Simon Glassf38a6422016-07-27 20:33:01 -0600562 paths = self.GetPathList(False)
Simon Glass7e803e12014-12-01 17:34:06 -0700563 return path in paths
564
565 def ListArchs(self):
566 """List architectures with available toolchains to download"""
567 host_arch, archives = self.LocateArchUrl('list')
Trevor Woerner10a51622018-11-21 03:31:13 -0500568 re_arch = re.compile('[-a-z0-9.]*[-_]([^-]*)-.*')
Simon Glass7e803e12014-12-01 17:34:06 -0700569 arch_set = set()
570 for archive in archives:
571 # Remove the host architecture from the start
572 arch = re_arch.match(archive[len(host_arch):])
573 if arch:
Trevor Woerner10a51622018-11-21 03:31:13 -0500574 if arch.group(1) != '2.0' and arch.group(1) != '64':
575 arch_set.add(arch.group(1))
Simon Glass7e803e12014-12-01 17:34:06 -0700576 return sorted(arch_set)
577
578 def FetchAndInstall(self, arch):
579 """Fetch and install a new toolchain
580
581 arch:
582 Architecture to fetch, or 'list' to list
583 """
584 # Fist get the URL for this architecture
Simon Glass9f1ba0f2016-07-27 20:33:02 -0600585 col = terminal.Color()
Simon Glassf45d3742022-01-29 14:14:17 -0700586 print(col.build(col.BLUE, "Downloading toolchain for arch '%s'" % arch))
Simon Glass7e803e12014-12-01 17:34:06 -0700587 url = self.LocateArchUrl(arch)
588 if not url:
Simon Glassc78ed662019-10-31 07:42:53 -0600589 print(("Cannot find toolchain for arch '%s' - use 'list' to list" %
590 arch))
Simon Glass7e803e12014-12-01 17:34:06 -0700591 return 2
592 home = os.environ['HOME']
593 dest = os.path.join(home, '.buildman-toolchains')
594 if not os.path.exists(dest):
595 os.mkdir(dest)
596
597 # Download the tar file for this toolchain and unpack it
Simon Glass80025522022-01-29 14:14:04 -0700598 tarfile, tmpdir = tools.download(url, '.buildman')
Simon Glass7e803e12014-12-01 17:34:06 -0700599 if not tarfile:
600 return 1
Simon Glassf45d3742022-01-29 14:14:17 -0700601 print(col.build(col.GREEN, 'Unpacking to: %s' % dest), end=' ')
Simon Glass7e803e12014-12-01 17:34:06 -0700602 sys.stdout.flush()
603 path = self.Unpack(tarfile, dest)
604 os.remove(tarfile)
605 os.rmdir(tmpdir)
Simon Glassc78ed662019-10-31 07:42:53 -0600606 print()
Simon Glass7e803e12014-12-01 17:34:06 -0700607
608 # Check that the toolchain works
Simon Glassf45d3742022-01-29 14:14:17 -0700609 print(col.build(col.GREEN, 'Testing'))
Simon Glass7e803e12014-12-01 17:34:06 -0700610 dirpath = os.path.join(dest, path)
Simon Glassf6757502015-03-02 17:05:15 -0700611 compiler_fname_list = self.ScanPath(dirpath, True)
612 if not compiler_fname_list:
Simon Glassc78ed662019-10-31 07:42:53 -0600613 print('Could not locate C compiler - fetch failed.')
Simon Glass7e803e12014-12-01 17:34:06 -0700614 return 1
Simon Glassf6757502015-03-02 17:05:15 -0700615 if len(compiler_fname_list) != 1:
Simon Glassf45d3742022-01-29 14:14:17 -0700616 print(col.build(col.RED, 'Warning, ambiguous toolchains: %s' %
Simon Glassc78ed662019-10-31 07:42:53 -0600617 ', '.join(compiler_fname_list)))
Simon Glassf6757502015-03-02 17:05:15 -0700618 toolchain = Toolchain(compiler_fname_list[0], True, True)
Simon Glass7e803e12014-12-01 17:34:06 -0700619
620 # Make sure that it will be found by buildman
621 if not self.TestSettingsHasPath(dirpath):
Simon Glassc78ed662019-10-31 07:42:53 -0600622 print(("Adding 'download' to config file '%s'" %
623 bsettings.config_fname))
Simon Glass06b83a52023-07-19 17:49:05 -0600624 bsettings.set_item('toolchain', 'download', '%s/*/*' % dest)
Simon Glass7e803e12014-12-01 17:34:06 -0700625 return 0