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 17ff690df9SSimon GlassPRIORITY_CALC = 0 18ff690df9SSimon Glass 19827e37b5SSimon Glass# Simple class to collect links from a page 20827e37b5SSimon Glassclass MyHTMLParser(HTMLParser): 21827e37b5SSimon Glass def __init__(self, arch): 22827e37b5SSimon Glass """Create a new parser 23827e37b5SSimon Glass 24827e37b5SSimon Glass After the parser runs, self.links will be set to a list of the links 25827e37b5SSimon Glass to .xz archives found in the page, and self.arch_link will be set to 26827e37b5SSimon Glass the one for the given architecture (or None if not found). 27827e37b5SSimon Glass 28827e37b5SSimon Glass Args: 29827e37b5SSimon Glass arch: Architecture to search for 30827e37b5SSimon Glass """ 31827e37b5SSimon Glass HTMLParser.__init__(self) 32827e37b5SSimon Glass self.arch_link = None 33827e37b5SSimon Glass self.links = [] 34827e37b5SSimon Glass self._match = '_%s-' % arch 35827e37b5SSimon Glass 36827e37b5SSimon Glass def handle_starttag(self, tag, attrs): 37827e37b5SSimon Glass if tag == 'a': 38827e37b5SSimon Glass for tag, value in attrs: 39827e37b5SSimon Glass if tag == 'href': 40827e37b5SSimon Glass if value and value.endswith('.xz'): 41827e37b5SSimon Glass self.links.append(value) 42827e37b5SSimon Glass if self._match in value: 43827e37b5SSimon Glass self.arch_link = value 44827e37b5SSimon Glass 45827e37b5SSimon Glass 46fc3fe1c2SSimon Glassclass Toolchain: 47fc3fe1c2SSimon Glass """A single toolchain 48fc3fe1c2SSimon Glass 49fc3fe1c2SSimon Glass Public members: 50fc3fe1c2SSimon Glass gcc: Full path to C compiler 51fc3fe1c2SSimon Glass path: Directory path containing C compiler 52fc3fe1c2SSimon Glass cross: Cross compile string, e.g. 'arm-linux-' 53fc3fe1c2SSimon Glass arch: Architecture of toolchain as determined from the first 54fc3fe1c2SSimon Glass component of the filename. E.g. arm-linux-gcc becomes arm 55ff690df9SSimon Glass priority: Toolchain priority (0=highest, 20=lowest) 56fc3fe1c2SSimon Glass """ 57*608e399fSSimon Glass def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC, 58*608e399fSSimon Glass arch=None): 59fc3fe1c2SSimon Glass """Create a new toolchain object. 60fc3fe1c2SSimon Glass 61fc3fe1c2SSimon Glass Args: 62fc3fe1c2SSimon Glass fname: Filename of the gcc component 63fc3fe1c2SSimon Glass test: True to run the toolchain to test it 64ad24ebacSSimon Glass verbose: True to print out the information 65ff690df9SSimon Glass priority: Priority to use for this toolchain, or PRIORITY_CALC to 66ff690df9SSimon Glass calculate it 67fc3fe1c2SSimon Glass """ 68fc3fe1c2SSimon Glass self.gcc = fname 69fc3fe1c2SSimon Glass self.path = os.path.dirname(fname) 70b5324123SSimon Glass 71b5324123SSimon Glass # Find the CROSS_COMPILE prefix to use for U-Boot. For example, 72b5324123SSimon Glass # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'. 73b5324123SSimon Glass basename = os.path.basename(fname) 74b5324123SSimon Glass pos = basename.rfind('-') 75b5324123SSimon Glass self.cross = basename[:pos + 1] if pos != -1 else '' 76b5324123SSimon Glass 77b5324123SSimon Glass # The architecture is the first part of the name 78fc3fe1c2SSimon Glass pos = self.cross.find('-') 79*608e399fSSimon Glass if arch: 80*608e399fSSimon Glass self.arch = arch 81*608e399fSSimon Glass else: 82fc3fe1c2SSimon Glass self.arch = self.cross[:pos] if pos != -1 else 'sandbox' 83fc3fe1c2SSimon Glass 84bb1501f2SSimon Glass env = self.MakeEnvironment(False) 85fc3fe1c2SSimon Glass 86fc3fe1c2SSimon Glass # As a basic sanity check, run the C compiler with --version 87fc3fe1c2SSimon Glass cmd = [fname, '--version'] 88ff690df9SSimon Glass if priority == PRIORITY_CALC: 89ff690df9SSimon Glass self.priority = self.GetPriority(fname) 90ff690df9SSimon Glass else: 91ff690df9SSimon Glass self.priority = priority 92fc3fe1c2SSimon Glass if test: 938bb2bddcSStephen Warren result = command.RunPipe([cmd], capture=True, env=env, 948bb2bddcSStephen Warren raise_on_error=False) 95fc3fe1c2SSimon Glass self.ok = result.return_code == 0 96fc3fe1c2SSimon Glass if verbose: 97fc3fe1c2SSimon Glass print 'Tool chain test: ', 98fc3fe1c2SSimon Glass if self.ok: 99*608e399fSSimon Glass print "OK, arch='%s', priority %d" % (self.arch, 100*608e399fSSimon Glass self.priority) 101fc3fe1c2SSimon Glass else: 102fc3fe1c2SSimon Glass print 'BAD' 103fc3fe1c2SSimon Glass print 'Command: ', cmd 104fc3fe1c2SSimon Glass print result.stdout 105fc3fe1c2SSimon Glass print result.stderr 106fc3fe1c2SSimon Glass else: 107fc3fe1c2SSimon Glass self.ok = True 108fc3fe1c2SSimon Glass 109fc3fe1c2SSimon Glass def GetPriority(self, fname): 110fc3fe1c2SSimon Glass """Return the priority of the toolchain. 111fc3fe1c2SSimon Glass 112fc3fe1c2SSimon Glass Toolchains are ranked according to their suitability by their 113fc3fe1c2SSimon Glass filename prefix. 114fc3fe1c2SSimon Glass 115fc3fe1c2SSimon Glass Args: 116fc3fe1c2SSimon Glass fname: Filename of toolchain 117fc3fe1c2SSimon Glass Returns: 118ff690df9SSimon Glass Priority of toolchain, PRIORITY_CALC=highest, 20=lowest. 119fc3fe1c2SSimon Glass """ 1208708267fSMasahiro Yamada priority_list = ['-elf', '-unknown-linux-gnu', '-linux', 121fc3fe1c2SSimon Glass '-none-linux-gnueabi', '-uclinux', '-none-eabi', 122fc3fe1c2SSimon Glass '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux'] 123fc3fe1c2SSimon Glass for prio in range(len(priority_list)): 124fc3fe1c2SSimon Glass if priority_list[prio] in fname: 125ff690df9SSimon Glass return PRIORITY_CALC + prio 126ff690df9SSimon Glass return PRIORITY_CALC + prio 127fc3fe1c2SSimon Glass 128bb1501f2SSimon Glass def MakeEnvironment(self, full_path): 129fc3fe1c2SSimon Glass """Returns an environment for using the toolchain. 130fc3fe1c2SSimon Glass 131bb1501f2SSimon Glass Thie takes the current environment and adds CROSS_COMPILE so that 132bb1501f2SSimon Glass the tool chain will operate correctly. 133bb1501f2SSimon Glass 134bb1501f2SSimon Glass Args: 135bb1501f2SSimon Glass full_path: Return the full path in CROSS_COMPILE and don't set 136bb1501f2SSimon Glass PATH 137fc3fe1c2SSimon Glass """ 138fc3fe1c2SSimon Glass env = dict(os.environ) 139bb1501f2SSimon Glass if full_path: 140bb1501f2SSimon Glass env['CROSS_COMPILE'] = os.path.join(self.path, self.cross) 141bb1501f2SSimon Glass else: 142fc3fe1c2SSimon Glass env['CROSS_COMPILE'] = self.cross 143f210b587SSimon Glass env['PATH'] = self.path + ':' + env['PATH'] 144bb1501f2SSimon Glass 145fc3fe1c2SSimon Glass return env 146fc3fe1c2SSimon Glass 147fc3fe1c2SSimon Glass 148fc3fe1c2SSimon Glassclass Toolchains: 149fc3fe1c2SSimon Glass """Manage a list of toolchains for building U-Boot 150fc3fe1c2SSimon Glass 151fc3fe1c2SSimon Glass We select one toolchain for each architecture type 152fc3fe1c2SSimon Glass 153fc3fe1c2SSimon Glass Public members: 154fc3fe1c2SSimon Glass toolchains: Dict of Toolchain objects, keyed by architecture name 155fc3fe1c2SSimon Glass paths: List of paths to check for toolchains (may contain wildcards) 156fc3fe1c2SSimon Glass """ 157fc3fe1c2SSimon Glass 158fc3fe1c2SSimon Glass def __init__(self): 159fc3fe1c2SSimon Glass self.toolchains = {} 160fc3fe1c2SSimon Glass self.paths = [] 161d4144e45SSimon Glass self._make_flags = dict(bsettings.GetItems('make-flags')) 162d4144e45SSimon Glass 163827e37b5SSimon Glass def GetPathList(self): 164827e37b5SSimon Glass """Get a list of available toolchain paths 165827e37b5SSimon Glass 166827e37b5SSimon Glass Returns: 167827e37b5SSimon Glass List of strings, each a path to a toolchain mentioned in the 168827e37b5SSimon Glass [toolchain] section of the settings file. 169827e37b5SSimon Glass """ 1704281ad8eSSimon Glass toolchains = bsettings.GetItems('toolchain') 1714281ad8eSSimon Glass if not toolchains: 172ad24ebacSSimon Glass print ('Warning: No tool chains - please add a [toolchain] section' 173ad24ebacSSimon Glass ' to your buildman config file %s. See README for details' % 1741826a18dSMasahiro Yamada bsettings.config_fname) 1754281ad8eSSimon Glass 176827e37b5SSimon Glass paths = [] 1774281ad8eSSimon Glass for name, value in toolchains: 178fc3fe1c2SSimon Glass if '*' in value: 179827e37b5SSimon Glass paths += glob.glob(value) 180fc3fe1c2SSimon Glass else: 181827e37b5SSimon Glass paths.append(value) 182827e37b5SSimon Glass return paths 183827e37b5SSimon Glass 184827e37b5SSimon Glass def GetSettings(self): 185827e37b5SSimon Glass self.paths += self.GetPathList() 186fc3fe1c2SSimon Glass 187*608e399fSSimon Glass def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC, 188*608e399fSSimon Glass arch=None): 189fc3fe1c2SSimon Glass """Add a toolchain to our list 190fc3fe1c2SSimon Glass 191fc3fe1c2SSimon Glass We select the given toolchain as our preferred one for its 192fc3fe1c2SSimon Glass architecture if it is a higher priority than the others. 193fc3fe1c2SSimon Glass 194fc3fe1c2SSimon Glass Args: 195fc3fe1c2SSimon Glass fname: Filename of toolchain's gcc driver 196fc3fe1c2SSimon Glass test: True to run the toolchain to test it 197ff690df9SSimon Glass priority: Priority to use for this toolchain 198*608e399fSSimon Glass arch: Toolchain architecture, or None if not known 199fc3fe1c2SSimon Glass """ 200*608e399fSSimon Glass toolchain = Toolchain(fname, test, verbose, priority, arch) 201fc3fe1c2SSimon Glass add_it = toolchain.ok 202fc3fe1c2SSimon Glass if toolchain.arch in self.toolchains: 203fc3fe1c2SSimon Glass add_it = (toolchain.priority < 204fc3fe1c2SSimon Glass self.toolchains[toolchain.arch].priority) 205fc3fe1c2SSimon Glass if add_it: 206fc3fe1c2SSimon Glass self.toolchains[toolchain.arch] = toolchain 207ff690df9SSimon Glass elif verbose: 208ff690df9SSimon Glass print ("Toolchain '%s' at priority %d will be ignored because " 209ff690df9SSimon Glass "another toolchain for arch '%s' has priority %d" % 210ff690df9SSimon Glass (toolchain.gcc, toolchain.priority, toolchain.arch, 211ff690df9SSimon Glass self.toolchains[toolchain.arch].priority)) 212fc3fe1c2SSimon Glass 213827e37b5SSimon Glass def ScanPath(self, path, verbose): 214827e37b5SSimon Glass """Scan a path for a valid toolchain 215827e37b5SSimon Glass 216827e37b5SSimon Glass Args: 217827e37b5SSimon Glass path: Path to scan 218827e37b5SSimon Glass verbose: True to print out progress information 219827e37b5SSimon Glass Returns: 220827e37b5SSimon Glass Filename of C compiler if found, else None 221827e37b5SSimon Glass """ 222d9088983SAlbert ARIBAUD fnames = [] 223827e37b5SSimon Glass for subdir in ['.', 'bin', 'usr/bin']: 224827e37b5SSimon Glass dirname = os.path.join(path, subdir) 225827e37b5SSimon Glass if verbose: print " - looking in '%s'" % dirname 226827e37b5SSimon Glass for fname in glob.glob(dirname + '/*gcc'): 227827e37b5SSimon Glass if verbose: print " - found '%s'" % fname 228d9088983SAlbert ARIBAUD fnames.append(fname) 229d9088983SAlbert ARIBAUD return fnames 230827e37b5SSimon Glass 231827e37b5SSimon Glass 232fc3fe1c2SSimon Glass def Scan(self, verbose): 233fc3fe1c2SSimon Glass """Scan for available toolchains and select the best for each arch. 234fc3fe1c2SSimon Glass 235fc3fe1c2SSimon Glass We look for all the toolchains we can file, figure out the 236fc3fe1c2SSimon Glass architecture for each, and whether it works. Then we select the 237fc3fe1c2SSimon Glass highest priority toolchain for each arch. 238fc3fe1c2SSimon Glass 239fc3fe1c2SSimon Glass Args: 240fc3fe1c2SSimon Glass verbose: True to print out progress information 241fc3fe1c2SSimon Glass """ 242fc3fe1c2SSimon Glass if verbose: print 'Scanning for tool chains' 243fc3fe1c2SSimon Glass for path in self.paths: 244fc3fe1c2SSimon Glass if verbose: print " - scanning path '%s'" % path 245d9088983SAlbert ARIBAUD fnames = self.ScanPath(path, verbose) 246d9088983SAlbert ARIBAUD for fname in fnames: 247fc3fe1c2SSimon Glass self.Add(fname, True, verbose) 248fc3fe1c2SSimon Glass 249fc3fe1c2SSimon Glass def List(self): 250fc3fe1c2SSimon Glass """List out the selected toolchains for each architecture""" 251fc3fe1c2SSimon Glass print 'List of available toolchains (%d):' % len(self.toolchains) 252fc3fe1c2SSimon Glass if len(self.toolchains): 253fc3fe1c2SSimon Glass for key, value in sorted(self.toolchains.iteritems()): 254fc3fe1c2SSimon Glass print '%-10s: %s' % (key, value.gcc) 255fc3fe1c2SSimon Glass else: 256fc3fe1c2SSimon Glass print 'None' 257fc3fe1c2SSimon Glass 258fc3fe1c2SSimon Glass def Select(self, arch): 259fc3fe1c2SSimon Glass """Returns the toolchain for a given architecture 260fc3fe1c2SSimon Glass 261fc3fe1c2SSimon Glass Args: 262fc3fe1c2SSimon Glass args: Name of architecture (e.g. 'arm', 'ppc_8xx') 263fc3fe1c2SSimon Glass 264fc3fe1c2SSimon Glass returns: 265fc3fe1c2SSimon Glass toolchain object, or None if none found 266fc3fe1c2SSimon Glass """ 2679b83bfdcSSimon Glass for tag, value in bsettings.GetItems('toolchain-alias'): 2689b83bfdcSSimon Glass if arch == tag: 2699b83bfdcSSimon Glass for alias in value.split(): 2709b83bfdcSSimon Glass if alias in self.toolchains: 2719b83bfdcSSimon Glass return self.toolchains[alias] 272fc3fe1c2SSimon Glass 273fc3fe1c2SSimon Glass if not arch in self.toolchains: 274fc3fe1c2SSimon Glass raise ValueError, ("No tool chain found for arch '%s'" % arch) 275fc3fe1c2SSimon Glass return self.toolchains[arch] 2764281ad8eSSimon Glass 2774281ad8eSSimon Glass def ResolveReferences(self, var_dict, args): 2784281ad8eSSimon Glass """Resolve variable references in a string 2794281ad8eSSimon Glass 2804281ad8eSSimon Glass This converts ${blah} within the string to the value of blah. 2814281ad8eSSimon Glass This function works recursively. 2824281ad8eSSimon Glass 2834281ad8eSSimon Glass Args: 2844281ad8eSSimon Glass var_dict: Dictionary containing variables and their values 2854281ad8eSSimon Glass args: String containing make arguments 2864281ad8eSSimon Glass Returns: 2874281ad8eSSimon Glass Resolved string 2884281ad8eSSimon Glass 2894281ad8eSSimon Glass >>> bsettings.Setup() 2904281ad8eSSimon Glass >>> tcs = Toolchains() 2914281ad8eSSimon Glass >>> tcs.Add('fred', False) 2924281ad8eSSimon Glass >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ 2934281ad8eSSimon Glass 'second' : '2nd'} 2944281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') 2954281ad8eSSimon Glass 'this=OBLIQUE_set' 2964281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') 2974281ad8eSSimon Glass 'this=OBLIQUE_setfi2ndrstnd' 2984281ad8eSSimon Glass """ 299f60c9d4fSSimon Glass re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') 3004281ad8eSSimon Glass 3014281ad8eSSimon Glass while True: 3024281ad8eSSimon Glass m = re_var.search(args) 3034281ad8eSSimon Glass if not m: 3044281ad8eSSimon Glass break 3054281ad8eSSimon Glass lookup = m.group(0)[2:-1] 3064281ad8eSSimon Glass value = var_dict.get(lookup, '') 3074281ad8eSSimon Glass args = args[:m.start(0)] + value + args[m.end(0):] 3084281ad8eSSimon Glass return args 3094281ad8eSSimon Glass 3104281ad8eSSimon Glass def GetMakeArguments(self, board): 3114281ad8eSSimon Glass """Returns 'make' arguments for a given board 3124281ad8eSSimon Glass 3134281ad8eSSimon Glass The flags are in a section called 'make-flags'. Flags are named 3144281ad8eSSimon Glass after the target they represent, for example snapper9260=TESTING=1 3154281ad8eSSimon Glass will pass TESTING=1 to make when building the snapper9260 board. 3164281ad8eSSimon Glass 3174281ad8eSSimon Glass References to other boards can be added in the string also. For 3184281ad8eSSimon Glass example: 3194281ad8eSSimon Glass 3204281ad8eSSimon Glass [make-flags] 3214281ad8eSSimon Glass at91-boards=ENABLE_AT91_TEST=1 3224281ad8eSSimon Glass snapper9260=${at91-boards} BUILD_TAG=442 3234281ad8eSSimon Glass snapper9g45=${at91-boards} BUILD_TAG=443 3244281ad8eSSimon Glass 3254281ad8eSSimon Glass This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 3264281ad8eSSimon Glass and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. 3274281ad8eSSimon Glass 3284281ad8eSSimon Glass A special 'target' variable is set to the board target. 3294281ad8eSSimon Glass 3304281ad8eSSimon Glass Args: 3314281ad8eSSimon Glass board: Board object for the board to check. 3324281ad8eSSimon Glass Returns: 3334281ad8eSSimon Glass 'make' flags for that board, or '' if none 3344281ad8eSSimon Glass """ 3354281ad8eSSimon Glass self._make_flags['target'] = board.target 3364281ad8eSSimon Glass arg_str = self.ResolveReferences(self._make_flags, 3374281ad8eSSimon Glass self._make_flags.get(board.target, '')) 3384281ad8eSSimon Glass args = arg_str.split(' ') 3394281ad8eSSimon Glass i = 0 3404281ad8eSSimon Glass while i < len(args): 3414281ad8eSSimon Glass if not args[i]: 3424281ad8eSSimon Glass del args[i] 3434281ad8eSSimon Glass else: 3444281ad8eSSimon Glass i += 1 3454281ad8eSSimon Glass return args 346827e37b5SSimon Glass 347827e37b5SSimon Glass def LocateArchUrl(self, fetch_arch): 348827e37b5SSimon Glass """Find a toolchain available online 349827e37b5SSimon Glass 350827e37b5SSimon Glass Look in standard places for available toolchains. At present the 351827e37b5SSimon Glass only standard place is at kernel.org. 352827e37b5SSimon Glass 353827e37b5SSimon Glass Args: 354827e37b5SSimon Glass arch: Architecture to look for, or 'list' for all 355827e37b5SSimon Glass Returns: 356827e37b5SSimon Glass If fetch_arch is 'list', a tuple: 357827e37b5SSimon Glass Machine architecture (e.g. x86_64) 358827e37b5SSimon Glass List of toolchains 359827e37b5SSimon Glass else 360827e37b5SSimon Glass URL containing this toolchain, if avaialble, else None 361827e37b5SSimon Glass """ 362827e37b5SSimon Glass arch = command.OutputOneLine('uname', '-m') 363827e37b5SSimon Glass base = 'https://www.kernel.org/pub/tools/crosstool/files/bin' 3641246231cSMichal Simek versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4'] 365827e37b5SSimon Glass links = [] 366827e37b5SSimon Glass for version in versions: 367827e37b5SSimon Glass url = '%s/%s/%s/' % (base, arch, version) 368827e37b5SSimon Glass print 'Checking: %s' % url 369827e37b5SSimon Glass response = urllib2.urlopen(url) 370827e37b5SSimon Glass html = response.read() 371827e37b5SSimon Glass parser = MyHTMLParser(fetch_arch) 372827e37b5SSimon Glass parser.feed(html) 373827e37b5SSimon Glass if fetch_arch == 'list': 374827e37b5SSimon Glass links += parser.links 375827e37b5SSimon Glass elif parser.arch_link: 376827e37b5SSimon Glass return url + parser.arch_link 377827e37b5SSimon Glass if fetch_arch == 'list': 378827e37b5SSimon Glass return arch, links 379827e37b5SSimon Glass return None 380827e37b5SSimon Glass 381827e37b5SSimon Glass def Download(self, url): 382827e37b5SSimon Glass """Download a file to a temporary directory 383827e37b5SSimon Glass 384827e37b5SSimon Glass Args: 385827e37b5SSimon Glass url: URL to download 386827e37b5SSimon Glass Returns: 387827e37b5SSimon Glass Tuple: 388827e37b5SSimon Glass Temporary directory name 389827e37b5SSimon Glass Full path to the downloaded archive file in that directory, 390827e37b5SSimon Glass or None if there was an error while downloading 391827e37b5SSimon Glass """ 392ad24ebacSSimon Glass print 'Downloading: %s' % url 393827e37b5SSimon Glass leaf = url.split('/')[-1] 394827e37b5SSimon Glass tmpdir = tempfile.mkdtemp('.buildman') 395827e37b5SSimon Glass response = urllib2.urlopen(url) 396827e37b5SSimon Glass fname = os.path.join(tmpdir, leaf) 397827e37b5SSimon Glass fd = open(fname, 'wb') 398827e37b5SSimon Glass meta = response.info() 399ad24ebacSSimon Glass size = int(meta.getheaders('Content-Length')[0]) 400827e37b5SSimon Glass done = 0 401827e37b5SSimon Glass block_size = 1 << 16 402827e37b5SSimon Glass status = '' 403827e37b5SSimon Glass 404827e37b5SSimon Glass # Read the file in chunks and show progress as we go 405827e37b5SSimon Glass while True: 406827e37b5SSimon Glass buffer = response.read(block_size) 407827e37b5SSimon Glass if not buffer: 408827e37b5SSimon Glass print chr(8) * (len(status) + 1), '\r', 409827e37b5SSimon Glass break 410827e37b5SSimon Glass 411827e37b5SSimon Glass done += len(buffer) 412827e37b5SSimon Glass fd.write(buffer) 413ad24ebacSSimon Glass status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024, 414827e37b5SSimon Glass done * 100 / size) 415827e37b5SSimon Glass status = status + chr(8) * (len(status) + 1) 416827e37b5SSimon Glass print status, 417827e37b5SSimon Glass sys.stdout.flush() 418827e37b5SSimon Glass fd.close() 419827e37b5SSimon Glass if done != size: 420827e37b5SSimon Glass print 'Error, failed to download' 421827e37b5SSimon Glass os.remove(fname) 422827e37b5SSimon Glass fname = None 423827e37b5SSimon Glass return tmpdir, fname 424827e37b5SSimon Glass 425827e37b5SSimon Glass def Unpack(self, fname, dest): 426827e37b5SSimon Glass """Unpack a tar file 427827e37b5SSimon Glass 428827e37b5SSimon Glass Args: 429827e37b5SSimon Glass fname: Filename to unpack 430827e37b5SSimon Glass dest: Destination directory 431827e37b5SSimon Glass Returns: 432827e37b5SSimon Glass Directory name of the first entry in the archive, without the 433827e37b5SSimon Glass trailing / 434827e37b5SSimon Glass """ 435827e37b5SSimon Glass stdout = command.Output('tar', 'xvfJ', fname, '-C', dest) 436827e37b5SSimon Glass return stdout.splitlines()[0][:-1] 437827e37b5SSimon Glass 438827e37b5SSimon Glass def TestSettingsHasPath(self, path): 439827e37b5SSimon Glass """Check if builmand will find this toolchain 440827e37b5SSimon Glass 441827e37b5SSimon Glass Returns: 442827e37b5SSimon Glass True if the path is in settings, False if not 443827e37b5SSimon Glass """ 444827e37b5SSimon Glass paths = self.GetPathList() 445827e37b5SSimon Glass return path in paths 446827e37b5SSimon Glass 447827e37b5SSimon Glass def ListArchs(self): 448827e37b5SSimon Glass """List architectures with available toolchains to download""" 449827e37b5SSimon Glass host_arch, archives = self.LocateArchUrl('list') 450827e37b5SSimon Glass re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*') 451827e37b5SSimon Glass arch_set = set() 452827e37b5SSimon Glass for archive in archives: 453827e37b5SSimon Glass # Remove the host architecture from the start 454827e37b5SSimon Glass arch = re_arch.match(archive[len(host_arch):]) 455827e37b5SSimon Glass if arch: 456827e37b5SSimon Glass arch_set.add(arch.group(1)) 457827e37b5SSimon Glass return sorted(arch_set) 458827e37b5SSimon Glass 459827e37b5SSimon Glass def FetchAndInstall(self, arch): 460827e37b5SSimon Glass """Fetch and install a new toolchain 461827e37b5SSimon Glass 462827e37b5SSimon Glass arch: 463827e37b5SSimon Glass Architecture to fetch, or 'list' to list 464827e37b5SSimon Glass """ 465827e37b5SSimon Glass # Fist get the URL for this architecture 466827e37b5SSimon Glass url = self.LocateArchUrl(arch) 467827e37b5SSimon Glass if not url: 468827e37b5SSimon Glass print ("Cannot find toolchain for arch '%s' - use 'list' to list" % 469827e37b5SSimon Glass arch) 470827e37b5SSimon Glass return 2 471827e37b5SSimon Glass home = os.environ['HOME'] 472827e37b5SSimon Glass dest = os.path.join(home, '.buildman-toolchains') 473827e37b5SSimon Glass if not os.path.exists(dest): 474827e37b5SSimon Glass os.mkdir(dest) 475827e37b5SSimon Glass 476827e37b5SSimon Glass # Download the tar file for this toolchain and unpack it 477827e37b5SSimon Glass tmpdir, tarfile = self.Download(url) 478827e37b5SSimon Glass if not tarfile: 479827e37b5SSimon Glass return 1 480827e37b5SSimon Glass print 'Unpacking to: %s' % dest, 481827e37b5SSimon Glass sys.stdout.flush() 482827e37b5SSimon Glass path = self.Unpack(tarfile, dest) 483827e37b5SSimon Glass os.remove(tarfile) 484827e37b5SSimon Glass os.rmdir(tmpdir) 485827e37b5SSimon Glass print 486827e37b5SSimon Glass 487827e37b5SSimon Glass # Check that the toolchain works 488827e37b5SSimon Glass print 'Testing' 489827e37b5SSimon Glass dirpath = os.path.join(dest, path) 4902a76a649SSimon Glass compiler_fname_list = self.ScanPath(dirpath, True) 4912a76a649SSimon Glass if not compiler_fname_list: 492827e37b5SSimon Glass print 'Could not locate C compiler - fetch failed.' 493827e37b5SSimon Glass return 1 4942a76a649SSimon Glass if len(compiler_fname_list) != 1: 4952a76a649SSimon Glass print ('Internal error, ambiguous toolchains: %s' % 4962a76a649SSimon Glass (', '.join(compiler_fname))) 4972a76a649SSimon Glass return 1 4982a76a649SSimon Glass toolchain = Toolchain(compiler_fname_list[0], True, True) 499827e37b5SSimon Glass 500827e37b5SSimon Glass # Make sure that it will be found by buildman 501827e37b5SSimon Glass if not self.TestSettingsHasPath(dirpath): 502827e37b5SSimon Glass print ("Adding 'download' to config file '%s'" % 503827e37b5SSimon Glass bsettings.config_fname) 504827e37b5SSimon Glass tools_dir = os.path.dirname(dirpath) 505827e37b5SSimon Glass bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir) 506827e37b5SSimon Glass return 0 507