blob: 79c7c11a1105742738c14a87200f8bcc7dde4e8d [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
Simon Glassd48a46c2014-12-01 17:34:00 -070093 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 Glassd48a46c2014-12-01 17:34:00 -0700175 def MakeEnvironment(self, full_path):
Simon Glassc05694f2013-04-03 11:07:16 +0000176 """Returns an environment for using the toolchain.
177
Simon Glassd48a46c2014-12-01 17:34:00 -0700178 Thie 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
180 output and possibly unicode encoded output of all build tools by
181 adding LC_ALL=C.
Simon Glassd48a46c2014-12-01 17:34:00 -0700182
Simon Glass93008e22021-04-11 16:27:28 +1200183 Note that os.environb is used to obtain the environment, since in some
184 cases the environment many contain non-ASCII characters and we see
185 errors like:
186
187 UnicodeEncodeError: 'utf-8' codec can't encode characters in position
188 569-570: surrogates not allowed
189
Simon Glassd48a46c2014-12-01 17:34:00 -0700190 Args:
191 full_path: Return the full path in CROSS_COMPILE and don't set
192 PATH
Simon Glassf77ca5b2019-01-07 16:44:20 -0700193 Returns:
Simon Glass93008e22021-04-11 16:27:28 +1200194 Dict containing the (bytes) environment to use. This is based on the
195 current environment, with changes as needed to CROSS_COMPILE, PATH
196 and LC_ALL.
Simon Glassc05694f2013-04-03 11:07:16 +0000197 """
Simon Glass93008e22021-04-11 16:27:28 +1200198 env = dict(os.environb)
York Sunfb197a82016-10-04 14:33:51 -0700199 wrapper = self.GetWrapper()
200
Simon Glassf77ca5b2019-01-07 16:44:20 -0700201 if self.override_toolchain:
202 # We'll use MakeArgs() to provide this
203 pass
204 elif full_path:
Simon Glass80025522022-01-29 14:14:04 -0700205 env[b'CROSS_COMPILE'] = tools.to_bytes(
Simon Glass93008e22021-04-11 16:27:28 +1200206 wrapper + os.path.join(self.path, self.cross))
Simon Glassd48a46c2014-12-01 17:34:00 -0700207 else:
Simon Glass80025522022-01-29 14:14:04 -0700208 env[b'CROSS_COMPILE'] = tools.to_bytes(wrapper + self.cross)
209 env[b'PATH'] = tools.to_bytes(self.path) + b':' + env[b'PATH']
Simon Glassd48a46c2014-12-01 17:34:00 -0700210
Simon Glass93008e22021-04-11 16:27:28 +1200211 env[b'LC_ALL'] = b'C'
Daniel Schwierzeckc9e5f0c2017-06-08 03:07:08 +0200212
Simon Glassc05694f2013-04-03 11:07:16 +0000213 return env
214
Simon Glassf77ca5b2019-01-07 16:44:20 -0700215 def MakeArgs(self):
216 """Create the 'make' arguments for a toolchain
217
218 This is only used when the toolchain is being overridden. Since the
219 U-Boot Makefile sets CC and HOSTCC explicitly we cannot rely on the
220 environment (and MakeEnvironment()) to override these values. This
221 function returns the arguments to accomplish this.
222
223 Returns:
224 List of arguments to pass to 'make'
225 """
226 if self.override_toolchain:
227 return ['HOSTCC=%s' % self.override_toolchain,
228 'CC=%s' % self.override_toolchain]
229 return []
230
Simon Glassc05694f2013-04-03 11:07:16 +0000231
232class Toolchains:
233 """Manage a list of toolchains for building U-Boot
234
235 We select one toolchain for each architecture type
236
237 Public members:
238 toolchains: Dict of Toolchain objects, keyed by architecture name
Simon Glassf5902732016-03-12 18:50:32 -0700239 prefixes: Dict of prefixes to check, keyed by architecture. This can
240 be a full path and toolchain prefix, for example
241 {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of
242 something on the search path, for example
243 {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported.
Simon Glassc05694f2013-04-03 11:07:16 +0000244 paths: List of paths to check for toolchains (may contain wildcards)
245 """
246
Simon Glassf77ca5b2019-01-07 16:44:20 -0700247 def __init__(self, override_toolchain=None):
Simon Glassc05694f2013-04-03 11:07:16 +0000248 self.toolchains = {}
Simon Glassf5902732016-03-12 18:50:32 -0700249 self.prefixes = {}
Simon Glassc05694f2013-04-03 11:07:16 +0000250 self.paths = []
Simon Glassf77ca5b2019-01-07 16:44:20 -0700251 self.override_toolchain = override_toolchain
Simon Glass06b83a52023-07-19 17:49:05 -0600252 self._make_flags = dict(bsettings.get_items('make-flags'))
Simon Glassed098bb2014-09-05 19:00:13 -0600253
Simon Glassf38a6422016-07-27 20:33:01 -0600254 def GetPathList(self, show_warning=True):
Simon Glass7e803e12014-12-01 17:34:06 -0700255 """Get a list of available toolchain paths
256
Simon Glassf38a6422016-07-27 20:33:01 -0600257 Args:
258 show_warning: True to show a warning if there are no tool chains.
259
Simon Glass7e803e12014-12-01 17:34:06 -0700260 Returns:
261 List of strings, each a path to a toolchain mentioned in the
262 [toolchain] section of the settings file.
263 """
Simon Glass06b83a52023-07-19 17:49:05 -0600264 toolchains = bsettings.get_items('toolchain')
Simon Glassf38a6422016-07-27 20:33:01 -0600265 if show_warning and not toolchains:
Simon Glassc78ed662019-10-31 07:42:53 -0600266 print(("Warning: No tool chains. Please run 'buildman "
Simon Glass9f1ba0f2016-07-27 20:33:02 -0600267 "--fetch-arch all' to download all available toolchains, or "
268 "add a [toolchain] section to your buildman config file "
Heinrich Schuchardtf04e8002023-01-19 16:22:10 +0100269 "%s. See buildman.rst for details" %
Simon Glassc78ed662019-10-31 07:42:53 -0600270 bsettings.config_fname))
Simon Glasscc246fb2013-09-23 17:35:17 -0600271
Simon Glass7e803e12014-12-01 17:34:06 -0700272 paths = []
Simon Glasscc246fb2013-09-23 17:35:17 -0600273 for name, value in toolchains:
Simon Glassc05694f2013-04-03 11:07:16 +0000274 if '*' in value:
Simon Glass7e803e12014-12-01 17:34:06 -0700275 paths += glob.glob(value)
Simon Glassc05694f2013-04-03 11:07:16 +0000276 else:
Simon Glass7e803e12014-12-01 17:34:06 -0700277 paths.append(value)
278 return paths
279
Simon Glassf38a6422016-07-27 20:33:01 -0600280 def GetSettings(self, show_warning=True):
281 """Get toolchain settings from the settings file.
282
283 Args:
284 show_warning: True to show a warning if there are no tool chains.
285 """
Simon Glass06b83a52023-07-19 17:49:05 -0600286 self.prefixes = bsettings.get_items('toolchain-prefix')
Simon Glassf38a6422016-07-27 20:33:01 -0600287 self.paths += self.GetPathList(show_warning)
Simon Glassc05694f2013-04-03 11:07:16 +0000288
Simon Glassc6cdd3e2016-03-06 19:45:38 -0700289 def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
290 arch=None):
Simon Glassc05694f2013-04-03 11:07:16 +0000291 """Add a toolchain to our list
292
293 We select the given toolchain as our preferred one for its
294 architecture if it is a higher priority than the others.
295
296 Args:
297 fname: Filename of toolchain's gcc driver
298 test: True to run the toolchain to test it
Simon Glass7b971292016-03-06 19:45:37 -0700299 priority: Priority to use for this toolchain
Simon Glassc6cdd3e2016-03-06 19:45:38 -0700300 arch: Toolchain architecture, or None if not known
Simon Glassc05694f2013-04-03 11:07:16 +0000301 """
Simon Glassf77ca5b2019-01-07 16:44:20 -0700302 toolchain = Toolchain(fname, test, verbose, priority, arch,
303 self.override_toolchain)
Simon Glassc05694f2013-04-03 11:07:16 +0000304 add_it = toolchain.ok
305 if toolchain.arch in self.toolchains:
306 add_it = (toolchain.priority <
307 self.toolchains[toolchain.arch].priority)
308 if add_it:
309 self.toolchains[toolchain.arch] = toolchain
Simon Glass7b971292016-03-06 19:45:37 -0700310 elif verbose:
Simon Glassc78ed662019-10-31 07:42:53 -0600311 print(("Toolchain '%s' at priority %d will be ignored because "
Simon Glass7b971292016-03-06 19:45:37 -0700312 "another toolchain for arch '%s' has priority %d" %
313 (toolchain.gcc, toolchain.priority, toolchain.arch,
Simon Glassc78ed662019-10-31 07:42:53 -0600314 self.toolchains[toolchain.arch].priority)))
Simon Glassc05694f2013-04-03 11:07:16 +0000315
Simon Glass7e803e12014-12-01 17:34:06 -0700316 def ScanPath(self, path, verbose):
317 """Scan a path for a valid toolchain
318
319 Args:
320 path: Path to scan
321 verbose: True to print out progress information
322 Returns:
323 Filename of C compiler if found, else None
324 """
Albert ARIBAUDd4f22cf2015-02-01 00:12:44 +0100325 fnames = []
Simon Glass7e803e12014-12-01 17:34:06 -0700326 for subdir in ['.', 'bin', 'usr/bin']:
327 dirname = os.path.join(path, subdir)
Simon Glassc78ed662019-10-31 07:42:53 -0600328 if verbose: print(" - looking in '%s'" % dirname)
Simon Glass7e803e12014-12-01 17:34:06 -0700329 for fname in glob.glob(dirname + '/*gcc'):
Simon Glassc78ed662019-10-31 07:42:53 -0600330 if verbose: print(" - found '%s'" % fname)
Albert ARIBAUDd4f22cf2015-02-01 00:12:44 +0100331 fnames.append(fname)
332 return fnames
Simon Glass7e803e12014-12-01 17:34:06 -0700333
Simon Glassf5902732016-03-12 18:50:32 -0700334 def ScanPathEnv(self, fname):
335 """Scan the PATH environment variable for a given filename.
336
337 Args:
338 fname: Filename to scan for
339 Returns:
340 List of matching pathanames, or [] if none
341 """
342 pathname_list = []
343 for path in os.environ["PATH"].split(os.pathsep):
344 path = path.strip('"')
345 pathname = os.path.join(path, fname)
346 if os.path.exists(pathname):
347 pathname_list.append(pathname)
348 return pathname_list
Simon Glass7e803e12014-12-01 17:34:06 -0700349
Simon Glassc05694f2013-04-03 11:07:16 +0000350 def Scan(self, verbose):
351 """Scan for available toolchains and select the best for each arch.
352
353 We look for all the toolchains we can file, figure out the
354 architecture for each, and whether it works. Then we select the
355 highest priority toolchain for each arch.
356
357 Args:
358 verbose: True to print out progress information
359 """
Simon Glassc78ed662019-10-31 07:42:53 -0600360 if verbose: print('Scanning for tool chains')
Simon Glassf5902732016-03-12 18:50:32 -0700361 for name, value in self.prefixes:
Simon Glassc78ed662019-10-31 07:42:53 -0600362 if verbose: print(" - scanning prefix '%s'" % value)
Simon Glassf5902732016-03-12 18:50:32 -0700363 if os.path.exists(value):
364 self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name)
365 continue
366 fname = value + 'gcc'
367 if os.path.exists(fname):
368 self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name)
369 continue
370 fname_list = self.ScanPathEnv(fname)
371 for f in fname_list:
372 self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name)
373 if not fname_list:
Simon Glassc78ed662019-10-31 07:42:53 -0600374 raise ValueError("No tool chain found for prefix '%s'" %
Simon Glassf5902732016-03-12 18:50:32 -0700375 value)
Simon Glassc05694f2013-04-03 11:07:16 +0000376 for path in self.paths:
Simon Glassc78ed662019-10-31 07:42:53 -0600377 if verbose: print(" - scanning path '%s'" % path)
Albert ARIBAUDd4f22cf2015-02-01 00:12:44 +0100378 fnames = self.ScanPath(path, verbose)
379 for fname in fnames:
Simon Glass7e803e12014-12-01 17:34:06 -0700380 self.Add(fname, True, verbose)
Simon Glassc05694f2013-04-03 11:07:16 +0000381
382 def List(self):
383 """List out the selected toolchains for each architecture"""
Simon Glass9f1ba0f2016-07-27 20:33:02 -0600384 col = terminal.Color()
Simon Glassf45d3742022-01-29 14:14:17 -0700385 print(col.build(col.BLUE, 'List of available toolchains (%d):' %
Simon Glassc78ed662019-10-31 07:42:53 -0600386 len(self.toolchains)))
Simon Glassc05694f2013-04-03 11:07:16 +0000387 if len(self.toolchains):
Simon Glassc78ed662019-10-31 07:42:53 -0600388 for key, value in sorted(self.toolchains.items()):
389 print('%-10s: %s' % (key, value.gcc))
Simon Glassc05694f2013-04-03 11:07:16 +0000390 else:
Simon Glassc78ed662019-10-31 07:42:53 -0600391 print('None')
Simon Glassc05694f2013-04-03 11:07:16 +0000392
393 def Select(self, arch):
394 """Returns the toolchain for a given architecture
395
396 Args:
397 args: Name of architecture (e.g. 'arm', 'ppc_8xx')
398
399 returns:
400 toolchain object, or None if none found
401 """
Simon Glass06b83a52023-07-19 17:49:05 -0600402 for tag, value in bsettings.get_items('toolchain-alias'):
Simon Glassc1528c12014-12-01 17:34:05 -0700403 if arch == tag:
404 for alias in value.split():
405 if alias in self.toolchains:
406 return self.toolchains[alias]
Simon Glassc05694f2013-04-03 11:07:16 +0000407
408 if not arch in self.toolchains:
Simon Glassc78ed662019-10-31 07:42:53 -0600409 raise ValueError("No tool chain found for arch '%s'" % arch)
Simon Glassc05694f2013-04-03 11:07:16 +0000410 return self.toolchains[arch]
Simon Glasscc246fb2013-09-23 17:35:17 -0600411
412 def ResolveReferences(self, var_dict, args):
413 """Resolve variable references in a string
414
415 This converts ${blah} within the string to the value of blah.
416 This function works recursively.
417
418 Args:
419 var_dict: Dictionary containing variables and their values
420 args: String containing make arguments
421 Returns:
422 Resolved string
423
Simon Glass06b83a52023-07-19 17:49:05 -0600424 >>> bsettings.setup(None)
Simon Glasscc246fb2013-09-23 17:35:17 -0600425 >>> tcs = Toolchains()
426 >>> tcs.Add('fred', False)
427 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
428 'second' : '2nd'}
429 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
430 'this=OBLIQUE_set'
431 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
432 'this=OBLIQUE_setfi2ndrstnd'
433 """
Simon Glass53e189d2014-08-28 09:43:40 -0600434 re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
Simon Glasscc246fb2013-09-23 17:35:17 -0600435
436 while True:
437 m = re_var.search(args)
438 if not m:
439 break
440 lookup = m.group(0)[2:-1]
441 value = var_dict.get(lookup, '')
442 args = args[:m.start(0)] + value + args[m.end(0):]
443 return args
444
Simon Glass8132f982022-07-11 19:03:57 -0600445 def GetMakeArguments(self, brd):
Simon Glasscc246fb2013-09-23 17:35:17 -0600446 """Returns 'make' arguments for a given board
447
448 The flags are in a section called 'make-flags'. Flags are named
449 after the target they represent, for example snapper9260=TESTING=1
450 will pass TESTING=1 to make when building the snapper9260 board.
451
452 References to other boards can be added in the string also. For
453 example:
454
455 [make-flags]
456 at91-boards=ENABLE_AT91_TEST=1
457 snapper9260=${at91-boards} BUILD_TAG=442
458 snapper9g45=${at91-boards} BUILD_TAG=443
459
460 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
461 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
462
463 A special 'target' variable is set to the board target.
464
465 Args:
Simon Glass8132f982022-07-11 19:03:57 -0600466 brd: Board object for the board to check.
Simon Glasscc246fb2013-09-23 17:35:17 -0600467 Returns:
468 'make' flags for that board, or '' if none
469 """
Simon Glass8132f982022-07-11 19:03:57 -0600470 self._make_flags['target'] = brd.target
Simon Glasscc246fb2013-09-23 17:35:17 -0600471 arg_str = self.ResolveReferences(self._make_flags,
Simon Glass8132f982022-07-11 19:03:57 -0600472 self._make_flags.get(brd.target, ''))
Cristian Ciocaltea9da17fb2019-11-24 22:30:26 +0200473 args = re.findall("(?:\".*?\"|\S)+", arg_str)
Simon Glasscc246fb2013-09-23 17:35:17 -0600474 i = 0
475 while i < len(args):
Cristian Ciocaltea9da17fb2019-11-24 22:30:26 +0200476 args[i] = args[i].replace('"', '')
Simon Glasscc246fb2013-09-23 17:35:17 -0600477 if not args[i]:
478 del args[i]
479 else:
480 i += 1
481 return args
Simon Glass7e803e12014-12-01 17:34:06 -0700482
483 def LocateArchUrl(self, fetch_arch):
484 """Find a toolchain available online
485
486 Look in standard places for available toolchains. At present the
487 only standard place is at kernel.org.
488
489 Args:
490 arch: Architecture to look for, or 'list' for all
491 Returns:
492 If fetch_arch is 'list', a tuple:
493 Machine architecture (e.g. x86_64)
494 List of toolchains
495 else
496 URL containing this toolchain, if avaialble, else None
497 """
Simon Glass840be732022-01-29 14:14:05 -0700498 arch = command.output_one_line('uname', '-m')
Matthias Bruggerfb50a262020-01-17 10:53:37 +0100499 if arch == 'aarch64':
500 arch = 'arm64'
Simon Glass7e803e12014-12-01 17:34:06 -0700501 base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
Tom Rini3bd5ed72023-08-25 13:21:26 -0400502 versions = ['13.2.0', '12.2.0']
Simon Glass7e803e12014-12-01 17:34:06 -0700503 links = []
504 for version in versions:
505 url = '%s/%s/%s/' % (base, arch, version)
Simon Glassc78ed662019-10-31 07:42:53 -0600506 print('Checking: %s' % url)
507 response = urllib.request.urlopen(url)
Simon Glass80025522022-01-29 14:14:04 -0700508 html = tools.to_string(response.read())
Simon Glass7e803e12014-12-01 17:34:06 -0700509 parser = MyHTMLParser(fetch_arch)
510 parser.feed(html)
511 if fetch_arch == 'list':
512 links += parser.links
513 elif parser.arch_link:
514 return url + parser.arch_link
515 if fetch_arch == 'list':
516 return arch, links
517 return None
518
Simon Glass7e803e12014-12-01 17:34:06 -0700519 def Unpack(self, fname, dest):
520 """Unpack a tar file
521
522 Args:
523 fname: Filename to unpack
524 dest: Destination directory
525 Returns:
526 Directory name of the first entry in the archive, without the
527 trailing /
528 """
Simon Glass840be732022-01-29 14:14:05 -0700529 stdout = command.output('tar', 'xvfJ', fname, '-C', dest)
Trevor Woernere0557a72018-11-21 03:31:12 -0500530 dirs = stdout.splitlines()[1].split('/')[:2]
531 return '/'.join(dirs)
Simon Glass7e803e12014-12-01 17:34:06 -0700532
533 def TestSettingsHasPath(self, path):
Simon Glass43ea1282016-07-27 20:33:03 -0600534 """Check if buildman will find this toolchain
Simon Glass7e803e12014-12-01 17:34:06 -0700535
536 Returns:
537 True if the path is in settings, False if not
538 """
Simon Glassf38a6422016-07-27 20:33:01 -0600539 paths = self.GetPathList(False)
Simon Glass7e803e12014-12-01 17:34:06 -0700540 return path in paths
541
542 def ListArchs(self):
543 """List architectures with available toolchains to download"""
544 host_arch, archives = self.LocateArchUrl('list')
Trevor Woerner10a51622018-11-21 03:31:13 -0500545 re_arch = re.compile('[-a-z0-9.]*[-_]([^-]*)-.*')
Simon Glass7e803e12014-12-01 17:34:06 -0700546 arch_set = set()
547 for archive in archives:
548 # Remove the host architecture from the start
549 arch = re_arch.match(archive[len(host_arch):])
550 if arch:
Trevor Woerner10a51622018-11-21 03:31:13 -0500551 if arch.group(1) != '2.0' and arch.group(1) != '64':
552 arch_set.add(arch.group(1))
Simon Glass7e803e12014-12-01 17:34:06 -0700553 return sorted(arch_set)
554
555 def FetchAndInstall(self, arch):
556 """Fetch and install a new toolchain
557
558 arch:
559 Architecture to fetch, or 'list' to list
560 """
561 # Fist get the URL for this architecture
Simon Glass9f1ba0f2016-07-27 20:33:02 -0600562 col = terminal.Color()
Simon Glassf45d3742022-01-29 14:14:17 -0700563 print(col.build(col.BLUE, "Downloading toolchain for arch '%s'" % arch))
Simon Glass7e803e12014-12-01 17:34:06 -0700564 url = self.LocateArchUrl(arch)
565 if not url:
Simon Glassc78ed662019-10-31 07:42:53 -0600566 print(("Cannot find toolchain for arch '%s' - use 'list' to list" %
567 arch))
Simon Glass7e803e12014-12-01 17:34:06 -0700568 return 2
569 home = os.environ['HOME']
570 dest = os.path.join(home, '.buildman-toolchains')
571 if not os.path.exists(dest):
572 os.mkdir(dest)
573
574 # Download the tar file for this toolchain and unpack it
Simon Glass80025522022-01-29 14:14:04 -0700575 tarfile, tmpdir = tools.download(url, '.buildman')
Simon Glass7e803e12014-12-01 17:34:06 -0700576 if not tarfile:
577 return 1
Simon Glassf45d3742022-01-29 14:14:17 -0700578 print(col.build(col.GREEN, 'Unpacking to: %s' % dest), end=' ')
Simon Glass7e803e12014-12-01 17:34:06 -0700579 sys.stdout.flush()
580 path = self.Unpack(tarfile, dest)
581 os.remove(tarfile)
582 os.rmdir(tmpdir)
Simon Glassc78ed662019-10-31 07:42:53 -0600583 print()
Simon Glass7e803e12014-12-01 17:34:06 -0700584
585 # Check that the toolchain works
Simon Glassf45d3742022-01-29 14:14:17 -0700586 print(col.build(col.GREEN, 'Testing'))
Simon Glass7e803e12014-12-01 17:34:06 -0700587 dirpath = os.path.join(dest, path)
Simon Glassf6757502015-03-02 17:05:15 -0700588 compiler_fname_list = self.ScanPath(dirpath, True)
589 if not compiler_fname_list:
Simon Glassc78ed662019-10-31 07:42:53 -0600590 print('Could not locate C compiler - fetch failed.')
Simon Glass7e803e12014-12-01 17:34:06 -0700591 return 1
Simon Glassf6757502015-03-02 17:05:15 -0700592 if len(compiler_fname_list) != 1:
Simon Glassf45d3742022-01-29 14:14:17 -0700593 print(col.build(col.RED, 'Warning, ambiguous toolchains: %s' %
Simon Glassc78ed662019-10-31 07:42:53 -0600594 ', '.join(compiler_fname_list)))
Simon Glassf6757502015-03-02 17:05:15 -0700595 toolchain = Toolchain(compiler_fname_list[0], True, True)
Simon Glass7e803e12014-12-01 17:34:06 -0700596
597 # Make sure that it will be found by buildman
598 if not self.TestSettingsHasPath(dirpath):
Simon Glassc78ed662019-10-31 07:42:53 -0600599 print(("Adding 'download' to config file '%s'" %
600 bsettings.config_fname))
Simon Glass06b83a52023-07-19 17:49:05 -0600601 bsettings.set_item('toolchain', 'download', '%s/*/*' % dest)
Simon Glass7e803e12014-12-01 17:34:06 -0700602 return 0