blob: 47788762016b1026b31890c59619a63a18807b62 [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',
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000123 '-none-linux-gnueabi', '-uclinux', '-none-eabi',
124 '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
125 for prio in range(len(priority_list)):
126 if priority_list[prio] in fname:
Simon Glassff690df2016-03-06 19:45:37 -0700127 return PRIORITY_CALC + prio
128 return PRIORITY_CALC + prio
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000129
York Sund5fe0132016-10-04 14:33:51 -0700130 def GetWrapper(self, show_warning=True):
131 """Get toolchain wrapper from the setting file.
132 """
133 value = ''
134 for name, value in bsettings.GetItems('toolchain-wrapper'):
135 if not value:
136 print "Warning: Wrapper not found"
137 if value:
138 value = value + ' '
139
140 return value
141
Simon Glassbb1501f2014-12-01 17:34:00 -0700142 def MakeEnvironment(self, full_path):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000143 """Returns an environment for using the toolchain.
144
Simon Glassbb1501f2014-12-01 17:34:00 -0700145 Thie takes the current environment and adds CROSS_COMPILE so that
146 the tool chain will operate correctly.
147
148 Args:
149 full_path: Return the full path in CROSS_COMPILE and don't set
150 PATH
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000151 """
152 env = dict(os.environ)
York Sund5fe0132016-10-04 14:33:51 -0700153 wrapper = self.GetWrapper()
154
Simon Glassbb1501f2014-12-01 17:34:00 -0700155 if full_path:
York Sund5fe0132016-10-04 14:33:51 -0700156 env['CROSS_COMPILE'] = wrapper + os.path.join(self.path, self.cross)
Simon Glassbb1501f2014-12-01 17:34:00 -0700157 else:
York Sund5fe0132016-10-04 14:33:51 -0700158 env['CROSS_COMPILE'] = wrapper + self.cross
Simon Glassbb1501f2014-12-01 17:34:00 -0700159 env['PATH'] = self.path + ':' + env['PATH']
160
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000161 return env
162
163
164class Toolchains:
165 """Manage a list of toolchains for building U-Boot
166
167 We select one toolchain for each architecture type
168
169 Public members:
170 toolchains: Dict of Toolchain objects, keyed by architecture name
Simon Glass17bce662016-03-12 18:50:32 -0700171 prefixes: Dict of prefixes to check, keyed by architecture. This can
172 be a full path and toolchain prefix, for example
173 {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of
174 something on the search path, for example
175 {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported.
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000176 paths: List of paths to check for toolchains (may contain wildcards)
177 """
178
179 def __init__(self):
180 self.toolchains = {}
Simon Glass17bce662016-03-12 18:50:32 -0700181 self.prefixes = {}
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000182 self.paths = []
Simon Glassd4144e42014-09-05 19:00:13 -0600183 self._make_flags = dict(bsettings.GetItems('make-flags'))
184
Simon Glass80e6a482016-07-27 20:33:01 -0600185 def GetPathList(self, show_warning=True):
Simon Glass827e37b2014-12-01 17:34:06 -0700186 """Get a list of available toolchain paths
187
Simon Glass80e6a482016-07-27 20:33:01 -0600188 Args:
189 show_warning: True to show a warning if there are no tool chains.
190
Simon Glass827e37b2014-12-01 17:34:06 -0700191 Returns:
192 List of strings, each a path to a toolchain mentioned in the
193 [toolchain] section of the settings file.
194 """
Simon Glass4281ad82013-09-23 17:35:17 -0600195 toolchains = bsettings.GetItems('toolchain')
Simon Glass80e6a482016-07-27 20:33:01 -0600196 if show_warning and not toolchains:
Simon Glass713bea32016-07-27 20:33:02 -0600197 print ("Warning: No tool chains. Please run 'buildman "
198 "--fetch-arch all' to download all available toolchains, or "
199 "add a [toolchain] section to your buildman config file "
200 "%s. See README for details" %
201 bsettings.config_fname)
Simon Glass4281ad82013-09-23 17:35:17 -0600202
Simon Glass827e37b2014-12-01 17:34:06 -0700203 paths = []
Simon Glass4281ad82013-09-23 17:35:17 -0600204 for name, value in toolchains:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000205 if '*' in value:
Simon Glass827e37b2014-12-01 17:34:06 -0700206 paths += glob.glob(value)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000207 else:
Simon Glass827e37b2014-12-01 17:34:06 -0700208 paths.append(value)
209 return paths
210
Simon Glass80e6a482016-07-27 20:33:01 -0600211 def GetSettings(self, show_warning=True):
212 """Get toolchain settings from the settings file.
213
214 Args:
215 show_warning: True to show a warning if there are no tool chains.
216 """
217 self.prefixes = bsettings.GetItems('toolchain-prefix')
218 self.paths += self.GetPathList(show_warning)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000219
Simon Glass608e3992016-03-06 19:45:38 -0700220 def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
221 arch=None):
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000222 """Add a toolchain to our list
223
224 We select the given toolchain as our preferred one for its
225 architecture if it is a higher priority than the others.
226
227 Args:
228 fname: Filename of toolchain's gcc driver
229 test: True to run the toolchain to test it
Simon Glassff690df2016-03-06 19:45:37 -0700230 priority: Priority to use for this toolchain
Simon Glass608e3992016-03-06 19:45:38 -0700231 arch: Toolchain architecture, or None if not known
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000232 """
Simon Glass608e3992016-03-06 19:45:38 -0700233 toolchain = Toolchain(fname, test, verbose, priority, arch)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000234 add_it = toolchain.ok
235 if toolchain.arch in self.toolchains:
236 add_it = (toolchain.priority <
237 self.toolchains[toolchain.arch].priority)
238 if add_it:
239 self.toolchains[toolchain.arch] = toolchain
Simon Glassff690df2016-03-06 19:45:37 -0700240 elif verbose:
241 print ("Toolchain '%s' at priority %d will be ignored because "
242 "another toolchain for arch '%s' has priority %d" %
243 (toolchain.gcc, toolchain.priority, toolchain.arch,
244 self.toolchains[toolchain.arch].priority))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000245
Simon Glass827e37b2014-12-01 17:34:06 -0700246 def ScanPath(self, path, verbose):
247 """Scan a path for a valid toolchain
248
249 Args:
250 path: Path to scan
251 verbose: True to print out progress information
252 Returns:
253 Filename of C compiler if found, else None
254 """
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100255 fnames = []
Simon Glass827e37b2014-12-01 17:34:06 -0700256 for subdir in ['.', 'bin', 'usr/bin']:
257 dirname = os.path.join(path, subdir)
258 if verbose: print " - looking in '%s'" % dirname
259 for fname in glob.glob(dirname + '/*gcc'):
260 if verbose: print " - found '%s'" % fname
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100261 fnames.append(fname)
262 return fnames
Simon Glass827e37b2014-12-01 17:34:06 -0700263
Simon Glass17bce662016-03-12 18:50:32 -0700264 def ScanPathEnv(self, fname):
265 """Scan the PATH environment variable for a given filename.
266
267 Args:
268 fname: Filename to scan for
269 Returns:
270 List of matching pathanames, or [] if none
271 """
272 pathname_list = []
273 for path in os.environ["PATH"].split(os.pathsep):
274 path = path.strip('"')
275 pathname = os.path.join(path, fname)
276 if os.path.exists(pathname):
277 pathname_list.append(pathname)
278 return pathname_list
Simon Glass827e37b2014-12-01 17:34:06 -0700279
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000280 def Scan(self, verbose):
281 """Scan for available toolchains and select the best for each arch.
282
283 We look for all the toolchains we can file, figure out the
284 architecture for each, and whether it works. Then we select the
285 highest priority toolchain for each arch.
286
287 Args:
288 verbose: True to print out progress information
289 """
290 if verbose: print 'Scanning for tool chains'
Simon Glass17bce662016-03-12 18:50:32 -0700291 for name, value in self.prefixes:
292 if verbose: print " - scanning prefix '%s'" % value
293 if os.path.exists(value):
294 self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name)
295 continue
296 fname = value + 'gcc'
297 if os.path.exists(fname):
298 self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name)
299 continue
300 fname_list = self.ScanPathEnv(fname)
301 for f in fname_list:
302 self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name)
303 if not fname_list:
304 raise ValueError, ("No tool chain found for prefix '%s'" %
305 value)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000306 for path in self.paths:
307 if verbose: print " - scanning path '%s'" % path
Albert ARIBAUDd9088982015-02-01 00:12:44 +0100308 fnames = self.ScanPath(path, verbose)
309 for fname in fnames:
Simon Glass827e37b2014-12-01 17:34:06 -0700310 self.Add(fname, True, verbose)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000311
312 def List(self):
313 """List out the selected toolchains for each architecture"""
Simon Glass713bea32016-07-27 20:33:02 -0600314 col = terminal.Color()
315 print col.Color(col.BLUE, 'List of available toolchains (%d):' %
316 len(self.toolchains))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000317 if len(self.toolchains):
318 for key, value in sorted(self.toolchains.iteritems()):
319 print '%-10s: %s' % (key, value.gcc)
320 else:
321 print 'None'
322
323 def Select(self, arch):
324 """Returns the toolchain for a given architecture
325
326 Args:
327 args: Name of architecture (e.g. 'arm', 'ppc_8xx')
328
329 returns:
330 toolchain object, or None if none found
331 """
Simon Glass9b83bfd2014-12-01 17:34:05 -0700332 for tag, value in bsettings.GetItems('toolchain-alias'):
333 if arch == tag:
334 for alias in value.split():
335 if alias in self.toolchains:
336 return self.toolchains[alias]
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000337
338 if not arch in self.toolchains:
339 raise ValueError, ("No tool chain found for arch '%s'" % arch)
340 return self.toolchains[arch]
Simon Glass4281ad82013-09-23 17:35:17 -0600341
342 def ResolveReferences(self, var_dict, args):
343 """Resolve variable references in a string
344
345 This converts ${blah} within the string to the value of blah.
346 This function works recursively.
347
348 Args:
349 var_dict: Dictionary containing variables and their values
350 args: String containing make arguments
351 Returns:
352 Resolved string
353
354 >>> bsettings.Setup()
355 >>> tcs = Toolchains()
356 >>> tcs.Add('fred', False)
357 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
358 'second' : '2nd'}
359 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
360 'this=OBLIQUE_set'
361 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
362 'this=OBLIQUE_setfi2ndrstnd'
363 """
Simon Glassf60c9d42014-08-28 09:43:40 -0600364 re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
Simon Glass4281ad82013-09-23 17:35:17 -0600365
366 while True:
367 m = re_var.search(args)
368 if not m:
369 break
370 lookup = m.group(0)[2:-1]
371 value = var_dict.get(lookup, '')
372 args = args[:m.start(0)] + value + args[m.end(0):]
373 return args
374
375 def GetMakeArguments(self, board):
376 """Returns 'make' arguments for a given board
377
378 The flags are in a section called 'make-flags'. Flags are named
379 after the target they represent, for example snapper9260=TESTING=1
380 will pass TESTING=1 to make when building the snapper9260 board.
381
382 References to other boards can be added in the string also. For
383 example:
384
385 [make-flags]
386 at91-boards=ENABLE_AT91_TEST=1
387 snapper9260=${at91-boards} BUILD_TAG=442
388 snapper9g45=${at91-boards} BUILD_TAG=443
389
390 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
391 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
392
393 A special 'target' variable is set to the board target.
394
395 Args:
396 board: Board object for the board to check.
397 Returns:
398 'make' flags for that board, or '' if none
399 """
400 self._make_flags['target'] = board.target
401 arg_str = self.ResolveReferences(self._make_flags,
402 self._make_flags.get(board.target, ''))
403 args = arg_str.split(' ')
404 i = 0
405 while i < len(args):
406 if not args[i]:
407 del args[i]
408 else:
409 i += 1
410 return args
Simon Glass827e37b2014-12-01 17:34:06 -0700411
412 def LocateArchUrl(self, fetch_arch):
413 """Find a toolchain available online
414
415 Look in standard places for available toolchains. At present the
416 only standard place is at kernel.org.
417
418 Args:
419 arch: Architecture to look for, or 'list' for all
420 Returns:
421 If fetch_arch is 'list', a tuple:
422 Machine architecture (e.g. x86_64)
423 List of toolchains
424 else
425 URL containing this toolchain, if avaialble, else None
426 """
427 arch = command.OutputOneLine('uname', '-m')
428 base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
Michal Simek12462312015-04-20 11:46:24 +0200429 versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4']
Simon Glass827e37b2014-12-01 17:34:06 -0700430 links = []
431 for version in versions:
432 url = '%s/%s/%s/' % (base, arch, version)
433 print 'Checking: %s' % url
434 response = urllib2.urlopen(url)
435 html = response.read()
436 parser = MyHTMLParser(fetch_arch)
437 parser.feed(html)
438 if fetch_arch == 'list':
439 links += parser.links
440 elif parser.arch_link:
441 return url + parser.arch_link
442 if fetch_arch == 'list':
443 return arch, links
444 return None
445
446 def Download(self, url):
447 """Download a file to a temporary directory
448
449 Args:
450 url: URL to download
451 Returns:
452 Tuple:
453 Temporary directory name
454 Full path to the downloaded archive file in that directory,
455 or None if there was an error while downloading
456 """
Simon Glassad24eba2016-03-06 19:45:35 -0700457 print 'Downloading: %s' % url
Simon Glass827e37b2014-12-01 17:34:06 -0700458 leaf = url.split('/')[-1]
459 tmpdir = tempfile.mkdtemp('.buildman')
460 response = urllib2.urlopen(url)
461 fname = os.path.join(tmpdir, leaf)
462 fd = open(fname, 'wb')
463 meta = response.info()
Simon Glassad24eba2016-03-06 19:45:35 -0700464 size = int(meta.getheaders('Content-Length')[0])
Simon Glass827e37b2014-12-01 17:34:06 -0700465 done = 0
466 block_size = 1 << 16
467 status = ''
468
469 # Read the file in chunks and show progress as we go
470 while True:
471 buffer = response.read(block_size)
472 if not buffer:
473 print chr(8) * (len(status) + 1), '\r',
474 break
475
476 done += len(buffer)
477 fd.write(buffer)
Simon Glassad24eba2016-03-06 19:45:35 -0700478 status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024,
Simon Glass827e37b2014-12-01 17:34:06 -0700479 done * 100 / size)
480 status = status + chr(8) * (len(status) + 1)
481 print status,
482 sys.stdout.flush()
483 fd.close()
484 if done != size:
485 print 'Error, failed to download'
486 os.remove(fname)
487 fname = None
488 return tmpdir, fname
489
490 def Unpack(self, fname, dest):
491 """Unpack a tar file
492
493 Args:
494 fname: Filename to unpack
495 dest: Destination directory
496 Returns:
497 Directory name of the first entry in the archive, without the
498 trailing /
499 """
500 stdout = command.Output('tar', 'xvfJ', fname, '-C', dest)
501 return stdout.splitlines()[0][:-1]
502
503 def TestSettingsHasPath(self, path):
Simon Glass2289b272016-07-27 20:33:03 -0600504 """Check if buildman will find this toolchain
Simon Glass827e37b2014-12-01 17:34:06 -0700505
506 Returns:
507 True if the path is in settings, False if not
508 """
Simon Glass80e6a482016-07-27 20:33:01 -0600509 paths = self.GetPathList(False)
Simon Glass827e37b2014-12-01 17:34:06 -0700510 return path in paths
511
512 def ListArchs(self):
513 """List architectures with available toolchains to download"""
514 host_arch, archives = self.LocateArchUrl('list')
515 re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*')
516 arch_set = set()
517 for archive in archives:
518 # Remove the host architecture from the start
519 arch = re_arch.match(archive[len(host_arch):])
520 if arch:
521 arch_set.add(arch.group(1))
522 return sorted(arch_set)
523
524 def FetchAndInstall(self, arch):
525 """Fetch and install a new toolchain
526
527 arch:
528 Architecture to fetch, or 'list' to list
529 """
530 # Fist get the URL for this architecture
Simon Glass713bea32016-07-27 20:33:02 -0600531 col = terminal.Color()
532 print col.Color(col.BLUE, "Downloading toolchain for arch '%s'" % arch)
Simon Glass827e37b2014-12-01 17:34:06 -0700533 url = self.LocateArchUrl(arch)
534 if not url:
535 print ("Cannot find toolchain for arch '%s' - use 'list' to list" %
536 arch)
537 return 2
538 home = os.environ['HOME']
539 dest = os.path.join(home, '.buildman-toolchains')
540 if not os.path.exists(dest):
541 os.mkdir(dest)
542
543 # Download the tar file for this toolchain and unpack it
544 tmpdir, tarfile = self.Download(url)
545 if not tarfile:
546 return 1
Simon Glass713bea32016-07-27 20:33:02 -0600547 print col.Color(col.GREEN, 'Unpacking to: %s' % dest),
Simon Glass827e37b2014-12-01 17:34:06 -0700548 sys.stdout.flush()
549 path = self.Unpack(tarfile, dest)
550 os.remove(tarfile)
551 os.rmdir(tmpdir)
552 print
553
554 # Check that the toolchain works
Simon Glass713bea32016-07-27 20:33:02 -0600555 print col.Color(col.GREEN, 'Testing')
Simon Glass827e37b2014-12-01 17:34:06 -0700556 dirpath = os.path.join(dest, path)
Simon Glass2a76a642015-03-02 17:05:15 -0700557 compiler_fname_list = self.ScanPath(dirpath, True)
558 if not compiler_fname_list:
Simon Glass827e37b2014-12-01 17:34:06 -0700559 print 'Could not locate C compiler - fetch failed.'
560 return 1
Simon Glass2a76a642015-03-02 17:05:15 -0700561 if len(compiler_fname_list) != 1:
Simon Glass713bea32016-07-27 20:33:02 -0600562 print col.Color(col.RED, 'Warning, ambiguous toolchains: %s' %
563 ', '.join(compiler_fname_list))
Simon Glass2a76a642015-03-02 17:05:15 -0700564 toolchain = Toolchain(compiler_fname_list[0], True, True)
Simon Glass827e37b2014-12-01 17:34:06 -0700565
566 # Make sure that it will be found by buildman
567 if not self.TestSettingsHasPath(dirpath):
568 print ("Adding 'download' to config file '%s'" %
569 bsettings.config_fname)
Simon Glassc8785c52016-07-27 20:33:05 -0600570 bsettings.SetItem('toolchain', 'download', '%s/*/*' % dest)
Simon Glass827e37b2014-12-01 17:34:06 -0700571 return 0