1fc3fe1c2SSimon Glass# Copyright (c) 2012 The Chromium OS Authors. 2fc3fe1c2SSimon Glass# 31a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+ 4fc3fe1c2SSimon Glass# 5fc3fe1c2SSimon Glass 64281ad8eSSimon Glassimport re 7fc3fe1c2SSimon Glassimport glob 8827e37b5SSimon Glassfrom HTMLParser import HTMLParser 9fc3fe1c2SSimon Glassimport os 10827e37b5SSimon Glassimport sys 11827e37b5SSimon Glassimport tempfile 12827e37b5SSimon Glassimport urllib2 13fc3fe1c2SSimon Glass 14fc3fe1c2SSimon Glassimport bsettings 15fc3fe1c2SSimon Glassimport command 16fc3fe1c2SSimon Glass 17827e37b5SSimon Glass# Simple class to collect links from a page 18827e37b5SSimon Glassclass MyHTMLParser(HTMLParser): 19827e37b5SSimon Glass def __init__(self, arch): 20827e37b5SSimon Glass """Create a new parser 21827e37b5SSimon Glass 22827e37b5SSimon Glass After the parser runs, self.links will be set to a list of the links 23827e37b5SSimon Glass to .xz archives found in the page, and self.arch_link will be set to 24827e37b5SSimon Glass the one for the given architecture (or None if not found). 25827e37b5SSimon Glass 26827e37b5SSimon Glass Args: 27827e37b5SSimon Glass arch: Architecture to search for 28827e37b5SSimon Glass """ 29827e37b5SSimon Glass HTMLParser.__init__(self) 30827e37b5SSimon Glass self.arch_link = None 31827e37b5SSimon Glass self.links = [] 32827e37b5SSimon Glass self._match = '_%s-' % arch 33827e37b5SSimon Glass 34827e37b5SSimon Glass def handle_starttag(self, tag, attrs): 35827e37b5SSimon Glass if tag == 'a': 36827e37b5SSimon Glass for tag, value in attrs: 37827e37b5SSimon Glass if tag == 'href': 38827e37b5SSimon Glass if value and value.endswith('.xz'): 39827e37b5SSimon Glass self.links.append(value) 40827e37b5SSimon Glass if self._match in value: 41827e37b5SSimon Glass self.arch_link = value 42827e37b5SSimon Glass 43827e37b5SSimon Glass 44fc3fe1c2SSimon Glassclass Toolchain: 45fc3fe1c2SSimon Glass """A single toolchain 46fc3fe1c2SSimon Glass 47fc3fe1c2SSimon Glass Public members: 48fc3fe1c2SSimon Glass gcc: Full path to C compiler 49fc3fe1c2SSimon Glass path: Directory path containing C compiler 50fc3fe1c2SSimon Glass cross: Cross compile string, e.g. 'arm-linux-' 51fc3fe1c2SSimon Glass arch: Architecture of toolchain as determined from the first 52fc3fe1c2SSimon Glass component of the filename. E.g. arm-linux-gcc becomes arm 53fc3fe1c2SSimon Glass """ 54fc3fe1c2SSimon Glass def __init__(self, fname, test, verbose=False): 55fc3fe1c2SSimon Glass """Create a new toolchain object. 56fc3fe1c2SSimon Glass 57fc3fe1c2SSimon Glass Args: 58fc3fe1c2SSimon Glass fname: Filename of the gcc component 59fc3fe1c2SSimon Glass test: True to run the toolchain to test it 60*ad24ebacSSimon Glass verbose: True to print out the information 61fc3fe1c2SSimon Glass """ 62fc3fe1c2SSimon Glass self.gcc = fname 63fc3fe1c2SSimon Glass self.path = os.path.dirname(fname) 64b5324123SSimon Glass 65b5324123SSimon Glass # Find the CROSS_COMPILE prefix to use for U-Boot. For example, 66b5324123SSimon Glass # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'. 67b5324123SSimon Glass basename = os.path.basename(fname) 68b5324123SSimon Glass pos = basename.rfind('-') 69b5324123SSimon Glass self.cross = basename[:pos + 1] if pos != -1 else '' 70b5324123SSimon Glass 71b5324123SSimon Glass # The architecture is the first part of the name 72fc3fe1c2SSimon Glass pos = self.cross.find('-') 73fc3fe1c2SSimon Glass self.arch = self.cross[:pos] if pos != -1 else 'sandbox' 74fc3fe1c2SSimon Glass 75bb1501f2SSimon Glass env = self.MakeEnvironment(False) 76fc3fe1c2SSimon Glass 77fc3fe1c2SSimon Glass # As a basic sanity check, run the C compiler with --version 78fc3fe1c2SSimon Glass cmd = [fname, '--version'] 79fc3fe1c2SSimon Glass if test: 808bb2bddcSStephen Warren result = command.RunPipe([cmd], capture=True, env=env, 818bb2bddcSStephen Warren raise_on_error=False) 82fc3fe1c2SSimon Glass self.ok = result.return_code == 0 83fc3fe1c2SSimon Glass if verbose: 84fc3fe1c2SSimon Glass print 'Tool chain test: ', 85fc3fe1c2SSimon Glass if self.ok: 86fc3fe1c2SSimon Glass print 'OK' 87fc3fe1c2SSimon Glass else: 88fc3fe1c2SSimon Glass print 'BAD' 89fc3fe1c2SSimon Glass print 'Command: ', cmd 90fc3fe1c2SSimon Glass print result.stdout 91fc3fe1c2SSimon Glass print result.stderr 92fc3fe1c2SSimon Glass else: 93fc3fe1c2SSimon Glass self.ok = True 94fc3fe1c2SSimon Glass self.priority = self.GetPriority(fname) 95fc3fe1c2SSimon Glass 96fc3fe1c2SSimon Glass def GetPriority(self, fname): 97fc3fe1c2SSimon Glass """Return the priority of the toolchain. 98fc3fe1c2SSimon Glass 99fc3fe1c2SSimon Glass Toolchains are ranked according to their suitability by their 100fc3fe1c2SSimon Glass filename prefix. 101fc3fe1c2SSimon Glass 102fc3fe1c2SSimon Glass Args: 103fc3fe1c2SSimon Glass fname: Filename of toolchain 104fc3fe1c2SSimon Glass Returns: 105fc3fe1c2SSimon Glass Priority of toolchain, 0=highest, 20=lowest. 106fc3fe1c2SSimon Glass """ 1078708267fSMasahiro Yamada priority_list = ['-elf', '-unknown-linux-gnu', '-linux', 108fc3fe1c2SSimon Glass '-none-linux-gnueabi', '-uclinux', '-none-eabi', 109fc3fe1c2SSimon Glass '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux'] 110fc3fe1c2SSimon Glass for prio in range(len(priority_list)): 111fc3fe1c2SSimon Glass if priority_list[prio] in fname: 112fc3fe1c2SSimon Glass return prio 113fc3fe1c2SSimon Glass return prio 114fc3fe1c2SSimon Glass 115bb1501f2SSimon Glass def MakeEnvironment(self, full_path): 116fc3fe1c2SSimon Glass """Returns an environment for using the toolchain. 117fc3fe1c2SSimon Glass 118bb1501f2SSimon Glass Thie takes the current environment and adds CROSS_COMPILE so that 119bb1501f2SSimon Glass the tool chain will operate correctly. 120bb1501f2SSimon Glass 121bb1501f2SSimon Glass Args: 122bb1501f2SSimon Glass full_path: Return the full path in CROSS_COMPILE and don't set 123bb1501f2SSimon Glass PATH 124fc3fe1c2SSimon Glass """ 125fc3fe1c2SSimon Glass env = dict(os.environ) 126bb1501f2SSimon Glass if full_path: 127bb1501f2SSimon Glass env['CROSS_COMPILE'] = os.path.join(self.path, self.cross) 128bb1501f2SSimon Glass else: 129fc3fe1c2SSimon Glass env['CROSS_COMPILE'] = self.cross 130f210b587SSimon Glass env['PATH'] = self.path + ':' + env['PATH'] 131bb1501f2SSimon Glass 132fc3fe1c2SSimon Glass return env 133fc3fe1c2SSimon Glass 134fc3fe1c2SSimon Glass 135fc3fe1c2SSimon Glassclass Toolchains: 136fc3fe1c2SSimon Glass """Manage a list of toolchains for building U-Boot 137fc3fe1c2SSimon Glass 138fc3fe1c2SSimon Glass We select one toolchain for each architecture type 139fc3fe1c2SSimon Glass 140fc3fe1c2SSimon Glass Public members: 141fc3fe1c2SSimon Glass toolchains: Dict of Toolchain objects, keyed by architecture name 142fc3fe1c2SSimon Glass paths: List of paths to check for toolchains (may contain wildcards) 143fc3fe1c2SSimon Glass """ 144fc3fe1c2SSimon Glass 145fc3fe1c2SSimon Glass def __init__(self): 146fc3fe1c2SSimon Glass self.toolchains = {} 147fc3fe1c2SSimon Glass self.paths = [] 148d4144e45SSimon Glass self._make_flags = dict(bsettings.GetItems('make-flags')) 149d4144e45SSimon Glass 150827e37b5SSimon Glass def GetPathList(self): 151827e37b5SSimon Glass """Get a list of available toolchain paths 152827e37b5SSimon Glass 153827e37b5SSimon Glass Returns: 154827e37b5SSimon Glass List of strings, each a path to a toolchain mentioned in the 155827e37b5SSimon Glass [toolchain] section of the settings file. 156827e37b5SSimon Glass """ 1574281ad8eSSimon Glass toolchains = bsettings.GetItems('toolchain') 1584281ad8eSSimon Glass if not toolchains: 159*ad24ebacSSimon Glass print ('Warning: No tool chains - please add a [toolchain] section' 160*ad24ebacSSimon Glass ' to your buildman config file %s. See README for details' % 1611826a18dSMasahiro Yamada bsettings.config_fname) 1624281ad8eSSimon Glass 163827e37b5SSimon Glass paths = [] 1644281ad8eSSimon Glass for name, value in toolchains: 165fc3fe1c2SSimon Glass if '*' in value: 166827e37b5SSimon Glass paths += glob.glob(value) 167fc3fe1c2SSimon Glass else: 168827e37b5SSimon Glass paths.append(value) 169827e37b5SSimon Glass return paths 170827e37b5SSimon Glass 171827e37b5SSimon Glass def GetSettings(self): 172827e37b5SSimon Glass self.paths += self.GetPathList() 173fc3fe1c2SSimon Glass 174fc3fe1c2SSimon Glass def Add(self, fname, test=True, verbose=False): 175fc3fe1c2SSimon Glass """Add a toolchain to our list 176fc3fe1c2SSimon Glass 177fc3fe1c2SSimon Glass We select the given toolchain as our preferred one for its 178fc3fe1c2SSimon Glass architecture if it is a higher priority than the others. 179fc3fe1c2SSimon Glass 180fc3fe1c2SSimon Glass Args: 181fc3fe1c2SSimon Glass fname: Filename of toolchain's gcc driver 182fc3fe1c2SSimon Glass test: True to run the toolchain to test it 183fc3fe1c2SSimon Glass """ 184fc3fe1c2SSimon Glass toolchain = Toolchain(fname, test, verbose) 185fc3fe1c2SSimon Glass add_it = toolchain.ok 186fc3fe1c2SSimon Glass if toolchain.arch in self.toolchains: 187fc3fe1c2SSimon Glass add_it = (toolchain.priority < 188fc3fe1c2SSimon Glass self.toolchains[toolchain.arch].priority) 189fc3fe1c2SSimon Glass if add_it: 190fc3fe1c2SSimon Glass self.toolchains[toolchain.arch] = toolchain 191fc3fe1c2SSimon Glass 192827e37b5SSimon Glass def ScanPath(self, path, verbose): 193827e37b5SSimon Glass """Scan a path for a valid toolchain 194827e37b5SSimon Glass 195827e37b5SSimon Glass Args: 196827e37b5SSimon Glass path: Path to scan 197827e37b5SSimon Glass verbose: True to print out progress information 198827e37b5SSimon Glass Returns: 199827e37b5SSimon Glass Filename of C compiler if found, else None 200827e37b5SSimon Glass """ 201d9088983SAlbert ARIBAUD fnames = [] 202827e37b5SSimon Glass for subdir in ['.', 'bin', 'usr/bin']: 203827e37b5SSimon Glass dirname = os.path.join(path, subdir) 204827e37b5SSimon Glass if verbose: print " - looking in '%s'" % dirname 205827e37b5SSimon Glass for fname in glob.glob(dirname + '/*gcc'): 206827e37b5SSimon Glass if verbose: print " - found '%s'" % fname 207d9088983SAlbert ARIBAUD fnames.append(fname) 208d9088983SAlbert ARIBAUD return fnames 209827e37b5SSimon Glass 210827e37b5SSimon Glass 211fc3fe1c2SSimon Glass def Scan(self, verbose): 212fc3fe1c2SSimon Glass """Scan for available toolchains and select the best for each arch. 213fc3fe1c2SSimon Glass 214fc3fe1c2SSimon Glass We look for all the toolchains we can file, figure out the 215fc3fe1c2SSimon Glass architecture for each, and whether it works. Then we select the 216fc3fe1c2SSimon Glass highest priority toolchain for each arch. 217fc3fe1c2SSimon Glass 218fc3fe1c2SSimon Glass Args: 219fc3fe1c2SSimon Glass verbose: True to print out progress information 220fc3fe1c2SSimon Glass """ 221fc3fe1c2SSimon Glass if verbose: print 'Scanning for tool chains' 222fc3fe1c2SSimon Glass for path in self.paths: 223fc3fe1c2SSimon Glass if verbose: print " - scanning path '%s'" % path 224d9088983SAlbert ARIBAUD fnames = self.ScanPath(path, verbose) 225d9088983SAlbert ARIBAUD for fname in fnames: 226fc3fe1c2SSimon Glass self.Add(fname, True, verbose) 227fc3fe1c2SSimon Glass 228fc3fe1c2SSimon Glass def List(self): 229fc3fe1c2SSimon Glass """List out the selected toolchains for each architecture""" 230fc3fe1c2SSimon Glass print 'List of available toolchains (%d):' % len(self.toolchains) 231fc3fe1c2SSimon Glass if len(self.toolchains): 232fc3fe1c2SSimon Glass for key, value in sorted(self.toolchains.iteritems()): 233fc3fe1c2SSimon Glass print '%-10s: %s' % (key, value.gcc) 234fc3fe1c2SSimon Glass else: 235fc3fe1c2SSimon Glass print 'None' 236fc3fe1c2SSimon Glass 237fc3fe1c2SSimon Glass def Select(self, arch): 238fc3fe1c2SSimon Glass """Returns the toolchain for a given architecture 239fc3fe1c2SSimon Glass 240fc3fe1c2SSimon Glass Args: 241fc3fe1c2SSimon Glass args: Name of architecture (e.g. 'arm', 'ppc_8xx') 242fc3fe1c2SSimon Glass 243fc3fe1c2SSimon Glass returns: 244fc3fe1c2SSimon Glass toolchain object, or None if none found 245fc3fe1c2SSimon Glass """ 2469b83bfdcSSimon Glass for tag, value in bsettings.GetItems('toolchain-alias'): 2479b83bfdcSSimon Glass if arch == tag: 2489b83bfdcSSimon Glass for alias in value.split(): 2499b83bfdcSSimon Glass if alias in self.toolchains: 2509b83bfdcSSimon Glass return self.toolchains[alias] 251fc3fe1c2SSimon Glass 252fc3fe1c2SSimon Glass if not arch in self.toolchains: 253fc3fe1c2SSimon Glass raise ValueError, ("No tool chain found for arch '%s'" % arch) 254fc3fe1c2SSimon Glass return self.toolchains[arch] 2554281ad8eSSimon Glass 2564281ad8eSSimon Glass def ResolveReferences(self, var_dict, args): 2574281ad8eSSimon Glass """Resolve variable references in a string 2584281ad8eSSimon Glass 2594281ad8eSSimon Glass This converts ${blah} within the string to the value of blah. 2604281ad8eSSimon Glass This function works recursively. 2614281ad8eSSimon Glass 2624281ad8eSSimon Glass Args: 2634281ad8eSSimon Glass var_dict: Dictionary containing variables and their values 2644281ad8eSSimon Glass args: String containing make arguments 2654281ad8eSSimon Glass Returns: 2664281ad8eSSimon Glass Resolved string 2674281ad8eSSimon Glass 2684281ad8eSSimon Glass >>> bsettings.Setup() 2694281ad8eSSimon Glass >>> tcs = Toolchains() 2704281ad8eSSimon Glass >>> tcs.Add('fred', False) 2714281ad8eSSimon Glass >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ 2724281ad8eSSimon Glass 'second' : '2nd'} 2734281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') 2744281ad8eSSimon Glass 'this=OBLIQUE_set' 2754281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') 2764281ad8eSSimon Glass 'this=OBLIQUE_setfi2ndrstnd' 2774281ad8eSSimon Glass """ 278f60c9d4fSSimon Glass re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') 2794281ad8eSSimon Glass 2804281ad8eSSimon Glass while True: 2814281ad8eSSimon Glass m = re_var.search(args) 2824281ad8eSSimon Glass if not m: 2834281ad8eSSimon Glass break 2844281ad8eSSimon Glass lookup = m.group(0)[2:-1] 2854281ad8eSSimon Glass value = var_dict.get(lookup, '') 2864281ad8eSSimon Glass args = args[:m.start(0)] + value + args[m.end(0):] 2874281ad8eSSimon Glass return args 2884281ad8eSSimon Glass 2894281ad8eSSimon Glass def GetMakeArguments(self, board): 2904281ad8eSSimon Glass """Returns 'make' arguments for a given board 2914281ad8eSSimon Glass 2924281ad8eSSimon Glass The flags are in a section called 'make-flags'. Flags are named 2934281ad8eSSimon Glass after the target they represent, for example snapper9260=TESTING=1 2944281ad8eSSimon Glass will pass TESTING=1 to make when building the snapper9260 board. 2954281ad8eSSimon Glass 2964281ad8eSSimon Glass References to other boards can be added in the string also. For 2974281ad8eSSimon Glass example: 2984281ad8eSSimon Glass 2994281ad8eSSimon Glass [make-flags] 3004281ad8eSSimon Glass at91-boards=ENABLE_AT91_TEST=1 3014281ad8eSSimon Glass snapper9260=${at91-boards} BUILD_TAG=442 3024281ad8eSSimon Glass snapper9g45=${at91-boards} BUILD_TAG=443 3034281ad8eSSimon Glass 3044281ad8eSSimon Glass This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 3054281ad8eSSimon Glass and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. 3064281ad8eSSimon Glass 3074281ad8eSSimon Glass A special 'target' variable is set to the board target. 3084281ad8eSSimon Glass 3094281ad8eSSimon Glass Args: 3104281ad8eSSimon Glass board: Board object for the board to check. 3114281ad8eSSimon Glass Returns: 3124281ad8eSSimon Glass 'make' flags for that board, or '' if none 3134281ad8eSSimon Glass """ 3144281ad8eSSimon Glass self._make_flags['target'] = board.target 3154281ad8eSSimon Glass arg_str = self.ResolveReferences(self._make_flags, 3164281ad8eSSimon Glass self._make_flags.get(board.target, '')) 3174281ad8eSSimon Glass args = arg_str.split(' ') 3184281ad8eSSimon Glass i = 0 3194281ad8eSSimon Glass while i < len(args): 3204281ad8eSSimon Glass if not args[i]: 3214281ad8eSSimon Glass del args[i] 3224281ad8eSSimon Glass else: 3234281ad8eSSimon Glass i += 1 3244281ad8eSSimon Glass return args 325827e37b5SSimon Glass 326827e37b5SSimon Glass def LocateArchUrl(self, fetch_arch): 327827e37b5SSimon Glass """Find a toolchain available online 328827e37b5SSimon Glass 329827e37b5SSimon Glass Look in standard places for available toolchains. At present the 330827e37b5SSimon Glass only standard place is at kernel.org. 331827e37b5SSimon Glass 332827e37b5SSimon Glass Args: 333827e37b5SSimon Glass arch: Architecture to look for, or 'list' for all 334827e37b5SSimon Glass Returns: 335827e37b5SSimon Glass If fetch_arch is 'list', a tuple: 336827e37b5SSimon Glass Machine architecture (e.g. x86_64) 337827e37b5SSimon Glass List of toolchains 338827e37b5SSimon Glass else 339827e37b5SSimon Glass URL containing this toolchain, if avaialble, else None 340827e37b5SSimon Glass """ 341827e37b5SSimon Glass arch = command.OutputOneLine('uname', '-m') 342827e37b5SSimon Glass base = 'https://www.kernel.org/pub/tools/crosstool/files/bin' 3431246231cSMichal Simek versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4'] 344827e37b5SSimon Glass links = [] 345827e37b5SSimon Glass for version in versions: 346827e37b5SSimon Glass url = '%s/%s/%s/' % (base, arch, version) 347827e37b5SSimon Glass print 'Checking: %s' % url 348827e37b5SSimon Glass response = urllib2.urlopen(url) 349827e37b5SSimon Glass html = response.read() 350827e37b5SSimon Glass parser = MyHTMLParser(fetch_arch) 351827e37b5SSimon Glass parser.feed(html) 352827e37b5SSimon Glass if fetch_arch == 'list': 353827e37b5SSimon Glass links += parser.links 354827e37b5SSimon Glass elif parser.arch_link: 355827e37b5SSimon Glass return url + parser.arch_link 356827e37b5SSimon Glass if fetch_arch == 'list': 357827e37b5SSimon Glass return arch, links 358827e37b5SSimon Glass return None 359827e37b5SSimon Glass 360827e37b5SSimon Glass def Download(self, url): 361827e37b5SSimon Glass """Download a file to a temporary directory 362827e37b5SSimon Glass 363827e37b5SSimon Glass Args: 364827e37b5SSimon Glass url: URL to download 365827e37b5SSimon Glass Returns: 366827e37b5SSimon Glass Tuple: 367827e37b5SSimon Glass Temporary directory name 368827e37b5SSimon Glass Full path to the downloaded archive file in that directory, 369827e37b5SSimon Glass or None if there was an error while downloading 370827e37b5SSimon Glass """ 371*ad24ebacSSimon Glass print 'Downloading: %s' % url 372827e37b5SSimon Glass leaf = url.split('/')[-1] 373827e37b5SSimon Glass tmpdir = tempfile.mkdtemp('.buildman') 374827e37b5SSimon Glass response = urllib2.urlopen(url) 375827e37b5SSimon Glass fname = os.path.join(tmpdir, leaf) 376827e37b5SSimon Glass fd = open(fname, 'wb') 377827e37b5SSimon Glass meta = response.info() 378*ad24ebacSSimon Glass size = int(meta.getheaders('Content-Length')[0]) 379827e37b5SSimon Glass done = 0 380827e37b5SSimon Glass block_size = 1 << 16 381827e37b5SSimon Glass status = '' 382827e37b5SSimon Glass 383827e37b5SSimon Glass # Read the file in chunks and show progress as we go 384827e37b5SSimon Glass while True: 385827e37b5SSimon Glass buffer = response.read(block_size) 386827e37b5SSimon Glass if not buffer: 387827e37b5SSimon Glass print chr(8) * (len(status) + 1), '\r', 388827e37b5SSimon Glass break 389827e37b5SSimon Glass 390827e37b5SSimon Glass done += len(buffer) 391827e37b5SSimon Glass fd.write(buffer) 392*ad24ebacSSimon Glass status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024, 393827e37b5SSimon Glass done * 100 / size) 394827e37b5SSimon Glass status = status + chr(8) * (len(status) + 1) 395827e37b5SSimon Glass print status, 396827e37b5SSimon Glass sys.stdout.flush() 397827e37b5SSimon Glass fd.close() 398827e37b5SSimon Glass if done != size: 399827e37b5SSimon Glass print 'Error, failed to download' 400827e37b5SSimon Glass os.remove(fname) 401827e37b5SSimon Glass fname = None 402827e37b5SSimon Glass return tmpdir, fname 403827e37b5SSimon Glass 404827e37b5SSimon Glass def Unpack(self, fname, dest): 405827e37b5SSimon Glass """Unpack a tar file 406827e37b5SSimon Glass 407827e37b5SSimon Glass Args: 408827e37b5SSimon Glass fname: Filename to unpack 409827e37b5SSimon Glass dest: Destination directory 410827e37b5SSimon Glass Returns: 411827e37b5SSimon Glass Directory name of the first entry in the archive, without the 412827e37b5SSimon Glass trailing / 413827e37b5SSimon Glass """ 414827e37b5SSimon Glass stdout = command.Output('tar', 'xvfJ', fname, '-C', dest) 415827e37b5SSimon Glass return stdout.splitlines()[0][:-1] 416827e37b5SSimon Glass 417827e37b5SSimon Glass def TestSettingsHasPath(self, path): 418827e37b5SSimon Glass """Check if builmand will find this toolchain 419827e37b5SSimon Glass 420827e37b5SSimon Glass Returns: 421827e37b5SSimon Glass True if the path is in settings, False if not 422827e37b5SSimon Glass """ 423827e37b5SSimon Glass paths = self.GetPathList() 424827e37b5SSimon Glass return path in paths 425827e37b5SSimon Glass 426827e37b5SSimon Glass def ListArchs(self): 427827e37b5SSimon Glass """List architectures with available toolchains to download""" 428827e37b5SSimon Glass host_arch, archives = self.LocateArchUrl('list') 429827e37b5SSimon Glass re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*') 430827e37b5SSimon Glass arch_set = set() 431827e37b5SSimon Glass for archive in archives: 432827e37b5SSimon Glass # Remove the host architecture from the start 433827e37b5SSimon Glass arch = re_arch.match(archive[len(host_arch):]) 434827e37b5SSimon Glass if arch: 435827e37b5SSimon Glass arch_set.add(arch.group(1)) 436827e37b5SSimon Glass return sorted(arch_set) 437827e37b5SSimon Glass 438827e37b5SSimon Glass def FetchAndInstall(self, arch): 439827e37b5SSimon Glass """Fetch and install a new toolchain 440827e37b5SSimon Glass 441827e37b5SSimon Glass arch: 442827e37b5SSimon Glass Architecture to fetch, or 'list' to list 443827e37b5SSimon Glass """ 444827e37b5SSimon Glass # Fist get the URL for this architecture 445827e37b5SSimon Glass url = self.LocateArchUrl(arch) 446827e37b5SSimon Glass if not url: 447827e37b5SSimon Glass print ("Cannot find toolchain for arch '%s' - use 'list' to list" % 448827e37b5SSimon Glass arch) 449827e37b5SSimon Glass return 2 450827e37b5SSimon Glass home = os.environ['HOME'] 451827e37b5SSimon Glass dest = os.path.join(home, '.buildman-toolchains') 452827e37b5SSimon Glass if not os.path.exists(dest): 453827e37b5SSimon Glass os.mkdir(dest) 454827e37b5SSimon Glass 455827e37b5SSimon Glass # Download the tar file for this toolchain and unpack it 456827e37b5SSimon Glass tmpdir, tarfile = self.Download(url) 457827e37b5SSimon Glass if not tarfile: 458827e37b5SSimon Glass return 1 459827e37b5SSimon Glass print 'Unpacking to: %s' % dest, 460827e37b5SSimon Glass sys.stdout.flush() 461827e37b5SSimon Glass path = self.Unpack(tarfile, dest) 462827e37b5SSimon Glass os.remove(tarfile) 463827e37b5SSimon Glass os.rmdir(tmpdir) 464827e37b5SSimon Glass print 465827e37b5SSimon Glass 466827e37b5SSimon Glass # Check that the toolchain works 467827e37b5SSimon Glass print 'Testing' 468827e37b5SSimon Glass dirpath = os.path.join(dest, path) 4692a76a649SSimon Glass compiler_fname_list = self.ScanPath(dirpath, True) 4702a76a649SSimon Glass if not compiler_fname_list: 471827e37b5SSimon Glass print 'Could not locate C compiler - fetch failed.' 472827e37b5SSimon Glass return 1 4732a76a649SSimon Glass if len(compiler_fname_list) != 1: 4742a76a649SSimon Glass print ('Internal error, ambiguous toolchains: %s' % 4752a76a649SSimon Glass (', '.join(compiler_fname))) 4762a76a649SSimon Glass return 1 4772a76a649SSimon Glass toolchain = Toolchain(compiler_fname_list[0], True, True) 478827e37b5SSimon Glass 479827e37b5SSimon Glass # Make sure that it will be found by buildman 480827e37b5SSimon Glass if not self.TestSettingsHasPath(dirpath): 481827e37b5SSimon Glass print ("Adding 'download' to config file '%s'" % 482827e37b5SSimon Glass bsettings.config_fname) 483827e37b5SSimon Glass tools_dir = os.path.dirname(dirpath) 484827e37b5SSimon Glass bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir) 485827e37b5SSimon Glass return 0 486