blob: 46a4e5ed409739232931b7e60bb7d34dfcddc151 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001# SPDX-License-Identifier: GPL-2.0+
Simon Glassfc3fe1c2013-04-03 11:07:16 +00002# Copyright (c) 2012 The Chromium OS Authors.
3#
Simon Glassfc3fe1c2013-04-03 11:07:16 +00004
Simon Glass4281ad82013-09-23 17:35:17 -06005import re
Simon Glassfc3fe1c2013-04-03 11:07:16 +00006import glob
Simon Glassc05aa032019-10-31 07:42:53 -06007from html.parser import HTMLParser
Simon Glassfc3fe1c2013-04-03 11:07:16 +00008import os
Simon Glass827e37b2014-12-01 17:34:06 -07009import sys
10import tempfile
Simon Glassc05aa032019-10-31 07:42:53 -060011import urllib.request, urllib.error, urllib.parse
Simon Glassfc3fe1c2013-04-03 11:07:16 +000012
Simon Glass0ede00f2020-04-17 18:09:02 -060013from buildman import bsettings
Simon Glassbf776672020-04-17 18:09:04 -060014from patman import command
15from patman import terminal
16from patman import tools
Simon Glassfc3fe1c2013-04-03 11:07:16 +000017
Simon Glass17bce662016-03-12 18:50:32 -070018(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH,
Simon Glassc05aa032019-10-31 07:42:53 -060019 PRIORITY_CALC) = list(range(4))
Simon Glassff690df2016-03-06 19:45:37 -070020
Simon Glass57cb9d52019-12-05 15:59:14 -070021(VAR_CROSS_COMPILE, VAR_PATH, VAR_ARCH, VAR_MAKE_ARGS) = range(4)
22
Simon Glass827e37b2014-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 Schwierzeck4c58d272018-05-10 07:15:53 -040038 self.re_arch = re.compile('[-_]%s-' % arch)
Simon Glass827e37b2014-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 Schwierzeck4c58d272018-05-10 07:15:53 -040046 if self.re_arch.search(value):
Simon Glass827e37b2014-12-01 17:34:06 -070047 self.arch_link = value
48
49
Simon Glassfc3fe1c2013-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 Glassff690df2016-03-06 19:45:37 -070059 priority: Toolchain priority (0=highest, 20=lowest)
Simon Glass00beb242019-01-07 16:44:20 -070060 override_toolchain: Toolchain to use for sandbox, overriding the normal
61 one
Simon Glassfc3fe1c2013-04-03 11:07:16 +000062 """
Simon Glass608e3992016-03-06 19:45:38 -070063 def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC,
Simon Glass00beb242019-01-07 16:44:20 -070064 arch=None, override_toolchain=None):
Simon Glassfc3fe1c2013-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 Glassad24eba2016-03-06 19:45:35 -070070 verbose: True to print out the information
Simon Glassff690df2016-03-06 19:45:37 -070071 priority: Priority to use for this toolchain, or PRIORITY_CALC to
72 calculate it
Simon Glassfc3fe1c2013-04-03 11:07:16 +000073 """
74 self.gcc = fname
75 self.path = os.path.dirname(fname)
Simon Glass00beb242019-01-07 16:44:20 -070076 self.override_toolchain = override_toolchain
Simon Glassb5324122014-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 Glassfc3fe1c2013-04-03 11:07:16 +000085 pos = self.cross.find('-')
Simon Glass608e3992016-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 Glass00beb242019-01-07 16:44:20 -070090 if self.arch == 'sandbox' and override_toolchain:
91 self.gcc = override_toolchain
Simon Glassfc3fe1c2013-04-03 11:07:16 +000092
Simon Glassbb1501f2014-12-01 17:34:00 -070093 env = self.MakeEnvironment(False)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000094
95 # As a basic sanity check, run the C compiler with --version
96 cmd = [fname, '--version']
Simon Glassff690df2016-03-06 19:45:37 -070097 if priority == PRIORITY_CALC:
98 self.priority = self.GetPriority(fname)
99 else:
100 self.priority = priority
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000101 if test:
Simon Glassd9800692022-01-29 14:14:05 -0700102 result = command.run_pipe([cmd], capture=True, env=env,
Stephen Warren8bb2bdd2013-10-09 14:28:09 -0600103 raise_on_error=False)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000104 self.ok = result.return_code == 0
105 if verbose:
Simon Glassc05aa032019-10-31 07:42:53 -0600106 print('Tool chain test: ', end=' ')
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000107 if self.ok:
Simon Glassc05aa032019-10-31 07:42:53 -0600108 print("OK, arch='%s', priority %d" % (self.arch,
109 self.priority))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000110 else:
Simon Glassc05aa032019-10-31 07:42:53 -0600111 print('BAD')
112 print('Command: ', cmd)
113 print(result.stdout)
114 print(result.stderr)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000115 else:
116 self.ok = True
Simon Glassfc3fe1c2013-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 Glassff690df2016-03-06 19:45:37 -0700127 Priority of toolchain, PRIORITY_CALC=highest, 20=lowest.
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000128 """
Masahiro Yamada87082672014-07-07 09:47:45 +0900129 priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
Tom Rini546a6f32017-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 Glassfc3fe1c2013-04-03 11:07:16 +0000133 for prio in range(len(priority_list)):
134 if priority_list[prio] in fname:
Simon Glassff690df2016-03-06 19:45:37 -0700135 return PRIORITY_CALC + prio
136 return PRIORITY_CALC + prio
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000137
York Sund5fe0132016-10-04 14:33:51 -0700138 def GetWrapper(self, show_warning=True):
139 """Get toolchain wrapper from the setting file.
140 """
Simon Glassccd29792019-01-07 16:44:24 -0700141 value = ''
142 for name, value in bsettings.GetItems('toolchain-wrapper'):
York Sund5fe0132016-10-04 14:33:51 -0700143 if not value:
Simon Glassc05aa032019-10-31 07:42:53 -0600144 print("Warning: Wrapper not found")
York Sund5fe0132016-10-04 14:33:51 -0700145 if value:
146 value = value + ' '
147
148 return value
149
Simon Glass57cb9d52019-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 """
159 wrapper = self.GetWrapper()
160 if which == VAR_CROSS_COMPILE:
161 return wrapper + os.path.join(self.path, self.cross)
162 elif which == VAR_PATH:
163 return self.path
164 elif which == VAR_ARCH:
165 return self.arch
166 elif which == VAR_MAKE_ARGS:
167 args = self.MakeArgs()
168 if args:
169 return ' '.join(args)
170 return ''
171 else:
172 raise ValueError('Unknown arg to GetEnvArgs (%d)' % which)
173
Simon Glassbb1501f2014-12-01 17:34:00 -0700174 def MakeEnvironment(self, full_path):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000175 """Returns an environment for using the toolchain.
176
Simon Glassbb1501f2014-12-01 17:34:00 -0700177 Thie takes the current environment and adds CROSS_COMPILE so that
Daniel Schwierzeckb0e994c2017-06-08 03:07:08 +0200178 the tool chain will operate correctly. This also disables localized
179 output and possibly unicode encoded output of all build tools by
180 adding LC_ALL=C.
Simon Glassbb1501f2014-12-01 17:34:00 -0700181
Simon Glassf1a83ab2021-04-11 16:27:28 +1200182 Note that os.environb is used to obtain the environment, since in some
183 cases the environment many contain non-ASCII characters and we see
184 errors like:
185
186 UnicodeEncodeError: 'utf-8' codec can't encode characters in position
187 569-570: surrogates not allowed
188
Simon Glassbb1501f2014-12-01 17:34:00 -0700189 Args:
190 full_path: Return the full path in CROSS_COMPILE and don't set
191 PATH
Simon Glass00beb242019-01-07 16:44:20 -0700192 Returns:
Simon Glassf1a83ab2021-04-11 16:27:28 +1200193 Dict containing the (bytes) environment to use. This is based on the
194 current environment, with changes as needed to CROSS_COMPILE, PATH
195 and LC_ALL.
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000196 """
Simon Glassf1a83ab2021-04-11 16:27:28 +1200197 env = dict(os.environb)
York Sund5fe0132016-10-04 14:33:51 -0700198 wrapper = self.GetWrapper()
199
Simon Glass00beb242019-01-07 16:44:20 -0700200 if self.override_toolchain:
201 # We'll use MakeArgs() to provide this
202 pass
203 elif full_path:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700204 env[b'CROSS_COMPILE'] = tools.to_bytes(
Simon Glassf1a83ab2021-04-11 16:27:28 +1200205 wrapper + os.path.join(self.path, self.cross))
Simon Glassbb1501f2014-12-01 17:34:00 -0700206 else:
Simon Glassc1aa66e2022-01-29 14:14:04 -0700207 env[b'CROSS_COMPILE'] = tools.to_bytes(wrapper + self.cross)
208 env[b'PATH'] = tools.to_bytes(self.path) + b':' + env[b'PATH']
Simon Glassbb1501f2014-12-01 17:34:00 -0700209
Simon Glassf1a83ab2021-04-11 16:27:28 +1200210 env[b'LC_ALL'] = b'C'
Daniel Schwierzeckb0e994c2017-06-08 03:07:08 +0200211
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000212 return env
213
Simon Glass00beb242019-01-07 16:44:20 -0700214 def MakeArgs(self):
215 """Create the 'make' arguments for a toolchain
216
217 This is only used when the toolchain is being overridden. Since the
218 U-Boot Makefile sets CC and HOSTCC explicitly we cannot rely on the
219 environment (and MakeEnvironment()) to override these values. This
220 function returns the arguments to accomplish this.
221
222 Returns:
223 List of arguments to pass to 'make'
224 """
225 if self.override_toolchain:
226 return ['HOSTCC=%s' % self.override_toolchain,
227 'CC=%s' % self.override_toolchain]
228 return []
229
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000230
231class Toolchains:
232 """Manage a list of toolchains for building U-Boot
233
234 We select one toolchain for each architecture type
235
236 Public members:
237 toolchains: Dict of Toolchain objects, keyed by architecture name
Simon Glass17bce662016-03-12 18:50:32 -0700238 prefixes: Dict of prefixes to check, keyed by architecture. This can
239 be a full path and toolchain prefix, for example
240 {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of
241 something on the search path, for example
242 {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported.
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000243 paths: List of paths to check for toolchains (may contain wildcards)
244 """
245
Simon Glass00beb242019-01-07 16:44:20 -0700246 def __init__(self, override_toolchain=None):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000247 self.toolchains = {}
Simon Glass17bce662016-03-12 18:50:32 -0700248 self.prefixes = {}
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000249 self.paths = []
Simon Glass00beb242019-01-07 16:44:20 -0700250 self.override_toolchain = override_toolchain
Simon Glassd4144e42014-09-05 19:00:13 -0600251 self._make_flags = dict(bsettings.GetItems('make-flags'))
252
Simon Glass80e6a482016-07-27 20:33:01 -0600253 def GetPathList(self, show_warning=True):
Simon Glass827e37b2014-12-01 17:34:06 -0700254 """Get a list of available toolchain paths
255
Simon Glass80e6a482016-07-27 20:33:01 -0600256 Args:
257 show_warning: True to show a warning if there are no tool chains.
258
Simon Glass827e37b2014-12-01 17:34:06 -0700259 Returns:
260 List of strings, each a path to a toolchain mentioned in the
261 [toolchain] section of the settings file.
262 """
Simon Glass4281ad82013-09-23 17:35:17 -0600263 toolchains = bsettings.GetItems('toolchain')
Simon Glass80e6a482016-07-27 20:33:01 -0600264 if show_warning and not toolchains:
Simon Glassc05aa032019-10-31 07:42:53 -0600265 print(("Warning: No tool chains. Please run 'buildman "
Simon Glass713bea32016-07-27 20:33:02 -0600266 "--fetch-arch all' to download all available toolchains, or "
267 "add a [toolchain] section to your buildman config file "
268 "%s. See README for details" %
Simon Glassc05aa032019-10-31 07:42:53 -0600269 bsettings.config_fname))
Simon Glass4281ad82013-09-23 17:35:17 -0600270
Simon Glass827e37b2014-12-01 17:34:06 -0700271 paths = []
Simon Glass4281ad82013-09-23 17:35:17 -0600272 for name, value in toolchains:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000273 if '*' in value:
Simon Glass827e37b2014-12-01 17:34:06 -0700274 paths += glob.glob(value)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000275 else:
Simon Glass827e37b2014-12-01 17:34:06 -0700276 paths.append(value)
277 return paths
278
Simon Glass80e6a482016-07-27 20:33:01 -0600279 def GetSettings(self, show_warning=True):
280 """Get toolchain settings from the settings file.
281
282 Args:
283 show_warning: True to show a warning if there are no tool chains.
284 """
285 self.prefixes = bsettings.GetItems('toolchain-prefix')
286 self.paths += self.GetPathList(show_warning)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000287
Simon Glass608e3992016-03-06 19:45:38 -0700288 def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
289 arch=None):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000290 """Add a toolchain to our list
291
292 We select the given toolchain as our preferred one for its
293 architecture if it is a higher priority than the others.
294
295 Args:
296 fname: Filename of toolchain's gcc driver
297 test: True to run the toolchain to test it
Simon Glassff690df2016-03-06 19:45:37 -0700298 priority: Priority to use for this toolchain
Simon Glass608e3992016-03-06 19:45:38 -0700299 arch: Toolchain architecture, or None if not known
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000300 """
Simon Glass00beb242019-01-07 16:44:20 -0700301 toolchain = Toolchain(fname, test, verbose, priority, arch,
302 self.override_toolchain)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000303 add_it = toolchain.ok
304 if toolchain.arch in self.toolchains:
305 add_it = (toolchain.priority <
306 self.toolchains[toolchain.arch].priority)
307 if add_it:
308 self.toolchains[toolchain.arch] = toolchain
Simon Glassff690df2016-03-06 19:45:37 -0700309 elif verbose:
Simon Glassc05aa032019-10-31 07:42:53 -0600310 print(("Toolchain '%s' at priority %d will be ignored because "
Simon Glassff690df2016-03-06 19:45:37 -0700311 "another toolchain for arch '%s' has priority %d" %
312 (toolchain.gcc, toolchain.priority, toolchain.arch,
Simon Glassc05aa032019-10-31 07:42:53 -0600313 self.toolchains[toolchain.arch].priority)))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000314
Simon Glass827e37b2014-12-01 17:34:06 -0700315 def ScanPath(self, path, verbose):
316 """Scan a path for a valid toolchain
317
318 Args:
319 path: Path to scan
320 verbose: True to print out progress information
321 Returns:
322 Filename of C compiler if found, else None
323 """
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100324 fnames = []
Simon Glass827e37b2014-12-01 17:34:06 -0700325 for subdir in ['.', 'bin', 'usr/bin']:
326 dirname = os.path.join(path, subdir)
Simon Glassc05aa032019-10-31 07:42:53 -0600327 if verbose: print(" - looking in '%s'" % dirname)
Simon Glass827e37b2014-12-01 17:34:06 -0700328 for fname in glob.glob(dirname + '/*gcc'):
Simon Glassc05aa032019-10-31 07:42:53 -0600329 if verbose: print(" - found '%s'" % fname)
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100330 fnames.append(fname)
331 return fnames
Simon Glass827e37b2014-12-01 17:34:06 -0700332
Simon Glass17bce662016-03-12 18:50:32 -0700333 def ScanPathEnv(self, fname):
334 """Scan the PATH environment variable for a given filename.
335
336 Args:
337 fname: Filename to scan for
338 Returns:
339 List of matching pathanames, or [] if none
340 """
341 pathname_list = []
342 for path in os.environ["PATH"].split(os.pathsep):
343 path = path.strip('"')
344 pathname = os.path.join(path, fname)
345 if os.path.exists(pathname):
346 pathname_list.append(pathname)
347 return pathname_list
Simon Glass827e37b2014-12-01 17:34:06 -0700348
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000349 def Scan(self, verbose):
350 """Scan for available toolchains and select the best for each arch.
351
352 We look for all the toolchains we can file, figure out the
353 architecture for each, and whether it works. Then we select the
354 highest priority toolchain for each arch.
355
356 Args:
357 verbose: True to print out progress information
358 """
Simon Glassc05aa032019-10-31 07:42:53 -0600359 if verbose: print('Scanning for tool chains')
Simon Glass17bce662016-03-12 18:50:32 -0700360 for name, value in self.prefixes:
Simon Glassc05aa032019-10-31 07:42:53 -0600361 if verbose: print(" - scanning prefix '%s'" % value)
Simon Glass17bce662016-03-12 18:50:32 -0700362 if os.path.exists(value):
363 self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name)
364 continue
365 fname = value + 'gcc'
366 if os.path.exists(fname):
367 self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name)
368 continue
369 fname_list = self.ScanPathEnv(fname)
370 for f in fname_list:
371 self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name)
372 if not fname_list:
Simon Glassc05aa032019-10-31 07:42:53 -0600373 raise ValueError("No tool chain found for prefix '%s'" %
Simon Glass17bce662016-03-12 18:50:32 -0700374 value)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000375 for path in self.paths:
Simon Glassc05aa032019-10-31 07:42:53 -0600376 if verbose: print(" - scanning path '%s'" % path)
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100377 fnames = self.ScanPath(path, verbose)
378 for fname in fnames:
Simon Glass827e37b2014-12-01 17:34:06 -0700379 self.Add(fname, True, verbose)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000380
381 def List(self):
382 """List out the selected toolchains for each architecture"""
Simon Glass713bea32016-07-27 20:33:02 -0600383 col = terminal.Color()
Simon Glass252ac582022-01-29 14:14:17 -0700384 print(col.build(col.BLUE, 'List of available toolchains (%d):' %
Simon Glassc05aa032019-10-31 07:42:53 -0600385 len(self.toolchains)))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000386 if len(self.toolchains):
Simon Glassc05aa032019-10-31 07:42:53 -0600387 for key, value in sorted(self.toolchains.items()):
388 print('%-10s: %s' % (key, value.gcc))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000389 else:
Simon Glassc05aa032019-10-31 07:42:53 -0600390 print('None')
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000391
392 def Select(self, arch):
393 """Returns the toolchain for a given architecture
394
395 Args:
396 args: Name of architecture (e.g. 'arm', 'ppc_8xx')
397
398 returns:
399 toolchain object, or None if none found
400 """
Simon Glass9b83bfd2014-12-01 17:34:05 -0700401 for tag, value in bsettings.GetItems('toolchain-alias'):
402 if arch == tag:
403 for alias in value.split():
404 if alias in self.toolchains:
405 return self.toolchains[alias]
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000406
407 if not arch in self.toolchains:
Simon Glassc05aa032019-10-31 07:42:53 -0600408 raise ValueError("No tool chain found for arch '%s'" % arch)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000409 return self.toolchains[arch]
Simon Glass4281ad82013-09-23 17:35:17 -0600410
411 def ResolveReferences(self, var_dict, args):
412 """Resolve variable references in a string
413
414 This converts ${blah} within the string to the value of blah.
415 This function works recursively.
416
417 Args:
418 var_dict: Dictionary containing variables and their values
419 args: String containing make arguments
420 Returns:
421 Resolved string
422
423 >>> bsettings.Setup()
424 >>> tcs = Toolchains()
425 >>> tcs.Add('fred', False)
426 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
427 'second' : '2nd'}
428 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
429 'this=OBLIQUE_set'
430 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
431 'this=OBLIQUE_setfi2ndrstnd'
432 """
Simon Glassf60c9d42014-08-28 09:43:40 -0600433 re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
Simon Glass4281ad82013-09-23 17:35:17 -0600434
435 while True:
436 m = re_var.search(args)
437 if not m:
438 break
439 lookup = m.group(0)[2:-1]
440 value = var_dict.get(lookup, '')
441 args = args[:m.start(0)] + value + args[m.end(0):]
442 return args
443
444 def GetMakeArguments(self, board):
445 """Returns 'make' arguments for a given board
446
447 The flags are in a section called 'make-flags'. Flags are named
448 after the target they represent, for example snapper9260=TESTING=1
449 will pass TESTING=1 to make when building the snapper9260 board.
450
451 References to other boards can be added in the string also. For
452 example:
453
454 [make-flags]
455 at91-boards=ENABLE_AT91_TEST=1
456 snapper9260=${at91-boards} BUILD_TAG=442
457 snapper9g45=${at91-boards} BUILD_TAG=443
458
459 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
460 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
461
462 A special 'target' variable is set to the board target.
463
464 Args:
465 board: Board object for the board to check.
466 Returns:
467 'make' flags for that board, or '' if none
468 """
469 self._make_flags['target'] = board.target
470 arg_str = self.ResolveReferences(self._make_flags,
471 self._make_flags.get(board.target, ''))
Cristian Ciocaltea4251fbc2019-11-24 22:30:26 +0200472 args = re.findall("(?:\".*?\"|\S)+", arg_str)
Simon Glass4281ad82013-09-23 17:35:17 -0600473 i = 0
474 while i < len(args):
Cristian Ciocaltea4251fbc2019-11-24 22:30:26 +0200475 args[i] = args[i].replace('"', '')
Simon Glass4281ad82013-09-23 17:35:17 -0600476 if not args[i]:
477 del args[i]
478 else:
479 i += 1
480 return args
Simon Glass827e37b2014-12-01 17:34:06 -0700481
482 def LocateArchUrl(self, fetch_arch):
483 """Find a toolchain available online
484
485 Look in standard places for available toolchains. At present the
486 only standard place is at kernel.org.
487
488 Args:
489 arch: Architecture to look for, or 'list' for all
490 Returns:
491 If fetch_arch is 'list', a tuple:
492 Machine architecture (e.g. x86_64)
493 List of toolchains
494 else
495 URL containing this toolchain, if avaialble, else None
496 """
Simon Glassd9800692022-01-29 14:14:05 -0700497 arch = command.output_one_line('uname', '-m')
Matthias Brugger2f7c53c2020-01-17 10:53:37 +0100498 if arch == 'aarch64':
499 arch = 'arm64'
Simon Glass827e37b2014-12-01 17:34:06 -0700500 base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
Tom Rinif7832ee2021-10-05 12:20:36 -0400501 versions = ['11.1.0', '9.2.0', '7.3.0', '6.4.0', '4.9.4']
Simon Glass827e37b2014-12-01 17:34:06 -0700502 links = []
503 for version in versions:
504 url = '%s/%s/%s/' % (base, arch, version)
Simon Glassc05aa032019-10-31 07:42:53 -0600505 print('Checking: %s' % url)
506 response = urllib.request.urlopen(url)
Simon Glassc1aa66e2022-01-29 14:14:04 -0700507 html = tools.to_string(response.read())
Simon Glass827e37b2014-12-01 17:34:06 -0700508 parser = MyHTMLParser(fetch_arch)
509 parser.feed(html)
510 if fetch_arch == 'list':
511 links += parser.links
512 elif parser.arch_link:
513 return url + parser.arch_link
514 if fetch_arch == 'list':
515 return arch, links
516 return None
517
Simon Glass827e37b2014-12-01 17:34:06 -0700518 def Unpack(self, fname, dest):
519 """Unpack a tar file
520
521 Args:
522 fname: Filename to unpack
523 dest: Destination directory
524 Returns:
525 Directory name of the first entry in the archive, without the
526 trailing /
527 """
Simon Glassd9800692022-01-29 14:14:05 -0700528 stdout = command.output('tar', 'xvfJ', fname, '-C', dest)
Trevor Woernerd82f5392018-11-21 03:31:12 -0500529 dirs = stdout.splitlines()[1].split('/')[:2]
530 return '/'.join(dirs)
Simon Glass827e37b2014-12-01 17:34:06 -0700531
532 def TestSettingsHasPath(self, path):
Simon Glass2289b272016-07-27 20:33:03 -0600533 """Check if buildman will find this toolchain
Simon Glass827e37b2014-12-01 17:34:06 -0700534
535 Returns:
536 True if the path is in settings, False if not
537 """
Simon Glass80e6a482016-07-27 20:33:01 -0600538 paths = self.GetPathList(False)
Simon Glass827e37b2014-12-01 17:34:06 -0700539 return path in paths
540
541 def ListArchs(self):
542 """List architectures with available toolchains to download"""
543 host_arch, archives = self.LocateArchUrl('list')
Trevor Woernerb11f1262018-11-21 03:31:13 -0500544 re_arch = re.compile('[-a-z0-9.]*[-_]([^-]*)-.*')
Simon Glass827e37b2014-12-01 17:34:06 -0700545 arch_set = set()
546 for archive in archives:
547 # Remove the host architecture from the start
548 arch = re_arch.match(archive[len(host_arch):])
549 if arch:
Trevor Woernerb11f1262018-11-21 03:31:13 -0500550 if arch.group(1) != '2.0' and arch.group(1) != '64':
551 arch_set.add(arch.group(1))
Simon Glass827e37b2014-12-01 17:34:06 -0700552 return sorted(arch_set)
553
554 def FetchAndInstall(self, arch):
555 """Fetch and install a new toolchain
556
557 arch:
558 Architecture to fetch, or 'list' to list
559 """
560 # Fist get the URL for this architecture
Simon Glass713bea32016-07-27 20:33:02 -0600561 col = terminal.Color()
Simon Glass252ac582022-01-29 14:14:17 -0700562 print(col.build(col.BLUE, "Downloading toolchain for arch '%s'" % arch))
Simon Glass827e37b2014-12-01 17:34:06 -0700563 url = self.LocateArchUrl(arch)
564 if not url:
Simon Glassc05aa032019-10-31 07:42:53 -0600565 print(("Cannot find toolchain for arch '%s' - use 'list' to list" %
566 arch))
Simon Glass827e37b2014-12-01 17:34:06 -0700567 return 2
568 home = os.environ['HOME']
569 dest = os.path.join(home, '.buildman-toolchains')
570 if not os.path.exists(dest):
571 os.mkdir(dest)
572
573 # Download the tar file for this toolchain and unpack it
Simon Glassc1aa66e2022-01-29 14:14:04 -0700574 tarfile, tmpdir = tools.download(url, '.buildman')
Simon Glass827e37b2014-12-01 17:34:06 -0700575 if not tarfile:
576 return 1
Simon Glass252ac582022-01-29 14:14:17 -0700577 print(col.build(col.GREEN, 'Unpacking to: %s' % dest), end=' ')
Simon Glass827e37b2014-12-01 17:34:06 -0700578 sys.stdout.flush()
579 path = self.Unpack(tarfile, dest)
580 os.remove(tarfile)
581 os.rmdir(tmpdir)
Simon Glassc05aa032019-10-31 07:42:53 -0600582 print()
Simon Glass827e37b2014-12-01 17:34:06 -0700583
584 # Check that the toolchain works
Simon Glass252ac582022-01-29 14:14:17 -0700585 print(col.build(col.GREEN, 'Testing'))
Simon Glass827e37b2014-12-01 17:34:06 -0700586 dirpath = os.path.join(dest, path)
Simon Glass2a76a642015-03-02 17:05:15 -0700587 compiler_fname_list = self.ScanPath(dirpath, True)
588 if not compiler_fname_list:
Simon Glassc05aa032019-10-31 07:42:53 -0600589 print('Could not locate C compiler - fetch failed.')
Simon Glass827e37b2014-12-01 17:34:06 -0700590 return 1
Simon Glass2a76a642015-03-02 17:05:15 -0700591 if len(compiler_fname_list) != 1:
Simon Glass252ac582022-01-29 14:14:17 -0700592 print(col.build(col.RED, 'Warning, ambiguous toolchains: %s' %
Simon Glassc05aa032019-10-31 07:42:53 -0600593 ', '.join(compiler_fname_list)))
Simon Glass2a76a642015-03-02 17:05:15 -0700594 toolchain = Toolchain(compiler_fname_list[0], True, True)
Simon Glass827e37b2014-12-01 17:34:06 -0700595
596 # Make sure that it will be found by buildman
597 if not self.TestSettingsHasPath(dirpath):
Simon Glassc05aa032019-10-31 07:42:53 -0600598 print(("Adding 'download' to config file '%s'" %
599 bsettings.config_fname))
Simon Glassc8785c52016-07-27 20:33:05 -0600600 bsettings.SetItem('toolchain', 'download', '%s/*/*' % dest)
Simon Glass827e37b2014-12-01 17:34:06 -0700601 return 0