blob: 2076323d5d39825f17b0460d09ce3dd383bc50b3 [file] [log] [blame]
Simon Glassfc3fe1c2013-04-03 11:07:16 +00001# Copyright (c) 2012 The Chromium OS Authors.
2#
Wolfgang Denk1a459662013-07-08 09:37:19 +02003# SPDX-License-Identifier: GPL-2.0+
Simon Glassfc3fe1c2013-04-03 11:07:16 +00004#
5
Simon Glass4281ad82013-09-23 17:35:17 -06006import re
Simon Glassfc3fe1c2013-04-03 11:07:16 +00007import glob
Simon Glass827e37b2014-12-01 17:34:06 -07008from HTMLParser import HTMLParser
Simon Glassfc3fe1c2013-04-03 11:07:16 +00009import os
Simon Glass827e37b2014-12-01 17:34:06 -070010import sys
11import tempfile
12import urllib2
Simon Glassfc3fe1c2013-04-03 11:07:16 +000013
14import bsettings
15import command
Simon Glass713bea32016-07-27 20:33:02 -060016import terminal
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,
19 PRIORITY_CALC) = range(4)
Simon Glassff690df2016-03-06 19:45:37 -070020
Simon Glass827e37b2014-12-01 17:34:06 -070021# Simple class to collect links from a page
22class MyHTMLParser(HTMLParser):
23 def __init__(self, arch):
24 """Create a new parser
25
26 After the parser runs, self.links will be set to a list of the links
27 to .xz archives found in the page, and self.arch_link will be set to
28 the one for the given architecture (or None if not found).
29
30 Args:
31 arch: Architecture to search for
32 """
33 HTMLParser.__init__(self)
34 self.arch_link = None
35 self.links = []
36 self._match = '_%s-' % arch
37
38 def handle_starttag(self, tag, attrs):
39 if tag == 'a':
40 for tag, value in attrs:
41 if tag == 'href':
42 if value and value.endswith('.xz'):
43 self.links.append(value)
44 if self._match in value:
45 self.arch_link = value
46
47
Simon Glassfc3fe1c2013-04-03 11:07:16 +000048class Toolchain:
49 """A single toolchain
50
51 Public members:
52 gcc: Full path to C compiler
53 path: Directory path containing C compiler
54 cross: Cross compile string, e.g. 'arm-linux-'
55 arch: Architecture of toolchain as determined from the first
56 component of the filename. E.g. arm-linux-gcc becomes arm
Simon Glassff690df2016-03-06 19:45:37 -070057 priority: Toolchain priority (0=highest, 20=lowest)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000058 """
Simon Glass608e3992016-03-06 19:45:38 -070059 def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC,
60 arch=None):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000061 """Create a new toolchain object.
62
63 Args:
64 fname: Filename of the gcc component
65 test: True to run the toolchain to test it
Simon Glassad24eba2016-03-06 19:45:35 -070066 verbose: True to print out the information
Simon Glassff690df2016-03-06 19:45:37 -070067 priority: Priority to use for this toolchain, or PRIORITY_CALC to
68 calculate it
Simon Glassfc3fe1c2013-04-03 11:07:16 +000069 """
70 self.gcc = fname
71 self.path = os.path.dirname(fname)
Simon Glassb5324122014-12-01 17:33:58 -070072
73 # Find the CROSS_COMPILE prefix to use for U-Boot. For example,
74 # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'.
75 basename = os.path.basename(fname)
76 pos = basename.rfind('-')
77 self.cross = basename[:pos + 1] if pos != -1 else ''
78
79 # The architecture is the first part of the name
Simon Glassfc3fe1c2013-04-03 11:07:16 +000080 pos = self.cross.find('-')
Simon Glass608e3992016-03-06 19:45:38 -070081 if arch:
82 self.arch = arch
83 else:
84 self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
Simon Glassfc3fe1c2013-04-03 11:07:16 +000085
Simon Glassbb1501f2014-12-01 17:34:00 -070086 env = self.MakeEnvironment(False)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000087
88 # As a basic sanity check, run the C compiler with --version
89 cmd = [fname, '--version']
Simon Glassff690df2016-03-06 19:45:37 -070090 if priority == PRIORITY_CALC:
91 self.priority = self.GetPriority(fname)
92 else:
93 self.priority = priority
Simon Glassfc3fe1c2013-04-03 11:07:16 +000094 if test:
Stephen Warren8bb2bdd2013-10-09 14:28:09 -060095 result = command.RunPipe([cmd], capture=True, env=env,
96 raise_on_error=False)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000097 self.ok = result.return_code == 0
98 if verbose:
99 print 'Tool chain test: ',
100 if self.ok:
Simon Glass608e3992016-03-06 19:45:38 -0700101 print "OK, arch='%s', priority %d" % (self.arch,
102 self.priority)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000103 else:
104 print 'BAD'
105 print 'Command: ', cmd
106 print result.stdout
107 print result.stderr
108 else:
109 self.ok = True
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000110
111 def GetPriority(self, fname):
112 """Return the priority of the toolchain.
113
114 Toolchains are ranked according to their suitability by their
115 filename prefix.
116
117 Args:
118 fname: Filename of toolchain
119 Returns:
Simon Glassff690df2016-03-06 19:45:37 -0700120 Priority of toolchain, PRIORITY_CALC=highest, 20=lowest.
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000121 """
Masahiro Yamada87082672014-07-07 09:47:45 +0900122 priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
Tom Rini546a6f32017-04-14 19:47:50 -0400123 '-none-linux-gnueabi', '-none-linux-gnueabihf', '-uclinux',
124 '-none-eabi', '-gentoo-linux-gnu', '-linux-gnueabi',
125 '-linux-gnueabihf', '-le-linux', '-uclinux']
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000126 for prio in range(len(priority_list)):
127 if priority_list[prio] in fname:
Simon Glassff690df2016-03-06 19:45:37 -0700128 return PRIORITY_CALC + prio
129 return PRIORITY_CALC + prio
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000130
York Sund5fe0132016-10-04 14:33:51 -0700131 def GetWrapper(self, show_warning=True):
132 """Get toolchain wrapper from the setting file.
133 """
134 value = ''
135 for name, value in bsettings.GetItems('toolchain-wrapper'):
136 if not value:
137 print "Warning: Wrapper not found"
138 if value:
139 value = value + ' '
140
141 return value
142
Simon Glassbb1501f2014-12-01 17:34:00 -0700143 def MakeEnvironment(self, full_path):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000144 """Returns an environment for using the toolchain.
145
Simon Glassbb1501f2014-12-01 17:34:00 -0700146 Thie takes the current environment and adds CROSS_COMPILE so that
Daniel Schwierzeckb0e994c2017-06-08 03:07:08 +0200147 the tool chain will operate correctly. This also disables localized
148 output and possibly unicode encoded output of all build tools by
149 adding LC_ALL=C.
Simon Glassbb1501f2014-12-01 17:34:00 -0700150
151 Args:
152 full_path: Return the full path in CROSS_COMPILE and don't set
153 PATH
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000154 """
155 env = dict(os.environ)
York Sund5fe0132016-10-04 14:33:51 -0700156 wrapper = self.GetWrapper()
157
Simon Glassbb1501f2014-12-01 17:34:00 -0700158 if full_path:
York Sund5fe0132016-10-04 14:33:51 -0700159 env['CROSS_COMPILE'] = wrapper + os.path.join(self.path, self.cross)
Simon Glassbb1501f2014-12-01 17:34:00 -0700160 else:
York Sund5fe0132016-10-04 14:33:51 -0700161 env['CROSS_COMPILE'] = wrapper + self.cross
Simon Glassbb1501f2014-12-01 17:34:00 -0700162 env['PATH'] = self.path + ':' + env['PATH']
163
Daniel Schwierzeckb0e994c2017-06-08 03:07:08 +0200164 env['LC_ALL'] = 'C'
165
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000166 return env
167
168
169class Toolchains:
170 """Manage a list of toolchains for building U-Boot
171
172 We select one toolchain for each architecture type
173
174 Public members:
175 toolchains: Dict of Toolchain objects, keyed by architecture name
Simon Glass17bce662016-03-12 18:50:32 -0700176 prefixes: Dict of prefixes to check, keyed by architecture. This can
177 be a full path and toolchain prefix, for example
178 {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of
179 something on the search path, for example
180 {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported.
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000181 paths: List of paths to check for toolchains (may contain wildcards)
182 """
183
184 def __init__(self):
185 self.toolchains = {}
Simon Glass17bce662016-03-12 18:50:32 -0700186 self.prefixes = {}
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000187 self.paths = []
Simon Glassd4144e42014-09-05 19:00:13 -0600188 self._make_flags = dict(bsettings.GetItems('make-flags'))
189
Simon Glass80e6a482016-07-27 20:33:01 -0600190 def GetPathList(self, show_warning=True):
Simon Glass827e37b2014-12-01 17:34:06 -0700191 """Get a list of available toolchain paths
192
Simon Glass80e6a482016-07-27 20:33:01 -0600193 Args:
194 show_warning: True to show a warning if there are no tool chains.
195
Simon Glass827e37b2014-12-01 17:34:06 -0700196 Returns:
197 List of strings, each a path to a toolchain mentioned in the
198 [toolchain] section of the settings file.
199 """
Simon Glass4281ad82013-09-23 17:35:17 -0600200 toolchains = bsettings.GetItems('toolchain')
Simon Glass80e6a482016-07-27 20:33:01 -0600201 if show_warning and not toolchains:
Simon Glass713bea32016-07-27 20:33:02 -0600202 print ("Warning: No tool chains. Please run 'buildman "
203 "--fetch-arch all' to download all available toolchains, or "
204 "add a [toolchain] section to your buildman config file "
205 "%s. See README for details" %
206 bsettings.config_fname)
Simon Glass4281ad82013-09-23 17:35:17 -0600207
Simon Glass827e37b2014-12-01 17:34:06 -0700208 paths = []
Simon Glass4281ad82013-09-23 17:35:17 -0600209 for name, value in toolchains:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000210 if '*' in value:
Simon Glass827e37b2014-12-01 17:34:06 -0700211 paths += glob.glob(value)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000212 else:
Simon Glass827e37b2014-12-01 17:34:06 -0700213 paths.append(value)
214 return paths
215
Simon Glass80e6a482016-07-27 20:33:01 -0600216 def GetSettings(self, show_warning=True):
217 """Get toolchain settings from the settings file.
218
219 Args:
220 show_warning: True to show a warning if there are no tool chains.
221 """
222 self.prefixes = bsettings.GetItems('toolchain-prefix')
223 self.paths += self.GetPathList(show_warning)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000224
Simon Glass608e3992016-03-06 19:45:38 -0700225 def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
226 arch=None):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000227 """Add a toolchain to our list
228
229 We select the given toolchain as our preferred one for its
230 architecture if it is a higher priority than the others.
231
232 Args:
233 fname: Filename of toolchain's gcc driver
234 test: True to run the toolchain to test it
Simon Glassff690df2016-03-06 19:45:37 -0700235 priority: Priority to use for this toolchain
Simon Glass608e3992016-03-06 19:45:38 -0700236 arch: Toolchain architecture, or None if not known
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000237 """
Simon Glass608e3992016-03-06 19:45:38 -0700238 toolchain = Toolchain(fname, test, verbose, priority, arch)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000239 add_it = toolchain.ok
240 if toolchain.arch in self.toolchains:
241 add_it = (toolchain.priority <
242 self.toolchains[toolchain.arch].priority)
243 if add_it:
244 self.toolchains[toolchain.arch] = toolchain
Simon Glassff690df2016-03-06 19:45:37 -0700245 elif verbose:
246 print ("Toolchain '%s' at priority %d will be ignored because "
247 "another toolchain for arch '%s' has priority %d" %
248 (toolchain.gcc, toolchain.priority, toolchain.arch,
249 self.toolchains[toolchain.arch].priority))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000250
Simon Glass827e37b2014-12-01 17:34:06 -0700251 def ScanPath(self, path, verbose):
252 """Scan a path for a valid toolchain
253
254 Args:
255 path: Path to scan
256 verbose: True to print out progress information
257 Returns:
258 Filename of C compiler if found, else None
259 """
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100260 fnames = []
Simon Glass827e37b2014-12-01 17:34:06 -0700261 for subdir in ['.', 'bin', 'usr/bin']:
262 dirname = os.path.join(path, subdir)
263 if verbose: print " - looking in '%s'" % dirname
264 for fname in glob.glob(dirname + '/*gcc'):
265 if verbose: print " - found '%s'" % fname
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100266 fnames.append(fname)
267 return fnames
Simon Glass827e37b2014-12-01 17:34:06 -0700268
Simon Glass17bce662016-03-12 18:50:32 -0700269 def ScanPathEnv(self, fname):
270 """Scan the PATH environment variable for a given filename.
271
272 Args:
273 fname: Filename to scan for
274 Returns:
275 List of matching pathanames, or [] if none
276 """
277 pathname_list = []
278 for path in os.environ["PATH"].split(os.pathsep):
279 path = path.strip('"')
280 pathname = os.path.join(path, fname)
281 if os.path.exists(pathname):
282 pathname_list.append(pathname)
283 return pathname_list
Simon Glass827e37b2014-12-01 17:34:06 -0700284
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000285 def Scan(self, verbose):
286 """Scan for available toolchains and select the best for each arch.
287
288 We look for all the toolchains we can file, figure out the
289 architecture for each, and whether it works. Then we select the
290 highest priority toolchain for each arch.
291
292 Args:
293 verbose: True to print out progress information
294 """
295 if verbose: print 'Scanning for tool chains'
Simon Glass17bce662016-03-12 18:50:32 -0700296 for name, value in self.prefixes:
297 if verbose: print " - scanning prefix '%s'" % value
298 if os.path.exists(value):
299 self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name)
300 continue
301 fname = value + 'gcc'
302 if os.path.exists(fname):
303 self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name)
304 continue
305 fname_list = self.ScanPathEnv(fname)
306 for f in fname_list:
307 self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name)
308 if not fname_list:
309 raise ValueError, ("No tool chain found for prefix '%s'" %
310 value)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000311 for path in self.paths:
312 if verbose: print " - scanning path '%s'" % path
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100313 fnames = self.ScanPath(path, verbose)
314 for fname in fnames:
Simon Glass827e37b2014-12-01 17:34:06 -0700315 self.Add(fname, True, verbose)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000316
317 def List(self):
318 """List out the selected toolchains for each architecture"""
Simon Glass713bea32016-07-27 20:33:02 -0600319 col = terminal.Color()
320 print col.Color(col.BLUE, 'List of available toolchains (%d):' %
321 len(self.toolchains))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000322 if len(self.toolchains):
323 for key, value in sorted(self.toolchains.iteritems()):
324 print '%-10s: %s' % (key, value.gcc)
325 else:
326 print 'None'
327
328 def Select(self, arch):
329 """Returns the toolchain for a given architecture
330
331 Args:
332 args: Name of architecture (e.g. 'arm', 'ppc_8xx')
333
334 returns:
335 toolchain object, or None if none found
336 """
Simon Glass9b83bfd2014-12-01 17:34:05 -0700337 for tag, value in bsettings.GetItems('toolchain-alias'):
338 if arch == tag:
339 for alias in value.split():
340 if alias in self.toolchains:
341 return self.toolchains[alias]
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000342
343 if not arch in self.toolchains:
344 raise ValueError, ("No tool chain found for arch '%s'" % arch)
345 return self.toolchains[arch]
Simon Glass4281ad82013-09-23 17:35:17 -0600346
347 def ResolveReferences(self, var_dict, args):
348 """Resolve variable references in a string
349
350 This converts ${blah} within the string to the value of blah.
351 This function works recursively.
352
353 Args:
354 var_dict: Dictionary containing variables and their values
355 args: String containing make arguments
356 Returns:
357 Resolved string
358
359 >>> bsettings.Setup()
360 >>> tcs = Toolchains()
361 >>> tcs.Add('fred', False)
362 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
363 'second' : '2nd'}
364 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
365 'this=OBLIQUE_set'
366 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
367 'this=OBLIQUE_setfi2ndrstnd'
368 """
Simon Glassf60c9d42014-08-28 09:43:40 -0600369 re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
Simon Glass4281ad82013-09-23 17:35:17 -0600370
371 while True:
372 m = re_var.search(args)
373 if not m:
374 break
375 lookup = m.group(0)[2:-1]
376 value = var_dict.get(lookup, '')
377 args = args[:m.start(0)] + value + args[m.end(0):]
378 return args
379
380 def GetMakeArguments(self, board):
381 """Returns 'make' arguments for a given board
382
383 The flags are in a section called 'make-flags'. Flags are named
384 after the target they represent, for example snapper9260=TESTING=1
385 will pass TESTING=1 to make when building the snapper9260 board.
386
387 References to other boards can be added in the string also. For
388 example:
389
390 [make-flags]
391 at91-boards=ENABLE_AT91_TEST=1
392 snapper9260=${at91-boards} BUILD_TAG=442
393 snapper9g45=${at91-boards} BUILD_TAG=443
394
395 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
396 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
397
398 A special 'target' variable is set to the board target.
399
400 Args:
401 board: Board object for the board to check.
402 Returns:
403 'make' flags for that board, or '' if none
404 """
405 self._make_flags['target'] = board.target
406 arg_str = self.ResolveReferences(self._make_flags,
407 self._make_flags.get(board.target, ''))
408 args = arg_str.split(' ')
409 i = 0
410 while i < len(args):
411 if not args[i]:
412 del args[i]
413 else:
414 i += 1
415 return args
Simon Glass827e37b2014-12-01 17:34:06 -0700416
417 def LocateArchUrl(self, fetch_arch):
418 """Find a toolchain available online
419
420 Look in standard places for available toolchains. At present the
421 only standard place is at kernel.org.
422
423 Args:
424 arch: Architecture to look for, or 'list' for all
425 Returns:
426 If fetch_arch is 'list', a tuple:
427 Machine architecture (e.g. x86_64)
428 List of toolchains
429 else
430 URL containing this toolchain, if avaialble, else None
431 """
432 arch = command.OutputOneLine('uname', '-m')
433 base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
Michal Simek12462312015-04-20 11:46:24 +0200434 versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4']
Simon Glass827e37b2014-12-01 17:34:06 -0700435 links = []
436 for version in versions:
437 url = '%s/%s/%s/' % (base, arch, version)
438 print 'Checking: %s' % url
439 response = urllib2.urlopen(url)
440 html = response.read()
441 parser = MyHTMLParser(fetch_arch)
442 parser.feed(html)
443 if fetch_arch == 'list':
444 links += parser.links
445 elif parser.arch_link:
446 return url + parser.arch_link
447 if fetch_arch == 'list':
448 return arch, links
449 return None
450
451 def Download(self, url):
452 """Download a file to a temporary directory
453
454 Args:
455 url: URL to download
456 Returns:
457 Tuple:
458 Temporary directory name
459 Full path to the downloaded archive file in that directory,
460 or None if there was an error while downloading
461 """
Simon Glassad24eba2016-03-06 19:45:35 -0700462 print 'Downloading: %s' % url
Simon Glass827e37b2014-12-01 17:34:06 -0700463 leaf = url.split('/')[-1]
464 tmpdir = tempfile.mkdtemp('.buildman')
465 response = urllib2.urlopen(url)
466 fname = os.path.join(tmpdir, leaf)
467 fd = open(fname, 'wb')
468 meta = response.info()
Simon Glassad24eba2016-03-06 19:45:35 -0700469 size = int(meta.getheaders('Content-Length')[0])
Simon Glass827e37b2014-12-01 17:34:06 -0700470 done = 0
471 block_size = 1 << 16
472 status = ''
473
474 # Read the file in chunks and show progress as we go
475 while True:
476 buffer = response.read(block_size)
477 if not buffer:
478 print chr(8) * (len(status) + 1), '\r',
479 break
480
481 done += len(buffer)
482 fd.write(buffer)
Simon Glassad24eba2016-03-06 19:45:35 -0700483 status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024,
Simon Glass827e37b2014-12-01 17:34:06 -0700484 done * 100 / size)
485 status = status + chr(8) * (len(status) + 1)
486 print status,
487 sys.stdout.flush()
488 fd.close()
489 if done != size:
490 print 'Error, failed to download'
491 os.remove(fname)
492 fname = None
493 return tmpdir, fname
494
495 def Unpack(self, fname, dest):
496 """Unpack a tar file
497
498 Args:
499 fname: Filename to unpack
500 dest: Destination directory
501 Returns:
502 Directory name of the first entry in the archive, without the
503 trailing /
504 """
505 stdout = command.Output('tar', 'xvfJ', fname, '-C', dest)
506 return stdout.splitlines()[0][:-1]
507
508 def TestSettingsHasPath(self, path):
Simon Glass2289b272016-07-27 20:33:03 -0600509 """Check if buildman will find this toolchain
Simon Glass827e37b2014-12-01 17:34:06 -0700510
511 Returns:
512 True if the path is in settings, False if not
513 """
Simon Glass80e6a482016-07-27 20:33:01 -0600514 paths = self.GetPathList(False)
Simon Glass827e37b2014-12-01 17:34:06 -0700515 return path in paths
516
517 def ListArchs(self):
518 """List architectures with available toolchains to download"""
519 host_arch, archives = self.LocateArchUrl('list')
520 re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*')
521 arch_set = set()
522 for archive in archives:
523 # Remove the host architecture from the start
524 arch = re_arch.match(archive[len(host_arch):])
525 if arch:
526 arch_set.add(arch.group(1))
527 return sorted(arch_set)
528
529 def FetchAndInstall(self, arch):
530 """Fetch and install a new toolchain
531
532 arch:
533 Architecture to fetch, or 'list' to list
534 """
535 # Fist get the URL for this architecture
Simon Glass713bea32016-07-27 20:33:02 -0600536 col = terminal.Color()
537 print col.Color(col.BLUE, "Downloading toolchain for arch '%s'" % arch)
Simon Glass827e37b2014-12-01 17:34:06 -0700538 url = self.LocateArchUrl(arch)
539 if not url:
540 print ("Cannot find toolchain for arch '%s' - use 'list' to list" %
541 arch)
542 return 2
543 home = os.environ['HOME']
544 dest = os.path.join(home, '.buildman-toolchains')
545 if not os.path.exists(dest):
546 os.mkdir(dest)
547
548 # Download the tar file for this toolchain and unpack it
549 tmpdir, tarfile = self.Download(url)
550 if not tarfile:
551 return 1
Simon Glass713bea32016-07-27 20:33:02 -0600552 print col.Color(col.GREEN, 'Unpacking to: %s' % dest),
Simon Glass827e37b2014-12-01 17:34:06 -0700553 sys.stdout.flush()
554 path = self.Unpack(tarfile, dest)
555 os.remove(tarfile)
556 os.rmdir(tmpdir)
557 print
558
559 # Check that the toolchain works
Simon Glass713bea32016-07-27 20:33:02 -0600560 print col.Color(col.GREEN, 'Testing')
Simon Glass827e37b2014-12-01 17:34:06 -0700561 dirpath = os.path.join(dest, path)
Simon Glass2a76a642015-03-02 17:05:15 -0700562 compiler_fname_list = self.ScanPath(dirpath, True)
563 if not compiler_fname_list:
Simon Glass827e37b2014-12-01 17:34:06 -0700564 print 'Could not locate C compiler - fetch failed.'
565 return 1
Simon Glass2a76a642015-03-02 17:05:15 -0700566 if len(compiler_fname_list) != 1:
Simon Glass713bea32016-07-27 20:33:02 -0600567 print col.Color(col.RED, 'Warning, ambiguous toolchains: %s' %
568 ', '.join(compiler_fname_list))
Simon Glass2a76a642015-03-02 17:05:15 -0700569 toolchain = Toolchain(compiler_fname_list[0], True, True)
Simon Glass827e37b2014-12-01 17:34:06 -0700570
571 # Make sure that it will be found by buildman
572 if not self.TestSettingsHasPath(dirpath):
573 print ("Adding 'download' to config file '%s'" %
574 bsettings.config_fname)
Simon Glassc8785c52016-07-27 20:33:05 -0600575 bsettings.SetItem('toolchain', 'download', '%s/*/*' % dest)
Simon Glass827e37b2014-12-01 17:34:06 -0700576 return 0