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 8*827e37b5SSimon Glassfrom HTMLParser import HTMLParser 9fc3fe1c2SSimon Glassimport os 10*827e37b5SSimon Glassimport sys 11*827e37b5SSimon Glassimport tempfile 12*827e37b5SSimon Glassimport urllib2 13fc3fe1c2SSimon Glass 14fc3fe1c2SSimon Glassimport bsettings 15fc3fe1c2SSimon Glassimport command 16fc3fe1c2SSimon Glass 17*827e37b5SSimon Glass# Simple class to collect links from a page 18*827e37b5SSimon Glassclass MyHTMLParser(HTMLParser): 19*827e37b5SSimon Glass def __init__(self, arch): 20*827e37b5SSimon Glass """Create a new parser 21*827e37b5SSimon Glass 22*827e37b5SSimon Glass After the parser runs, self.links will be set to a list of the links 23*827e37b5SSimon Glass to .xz archives found in the page, and self.arch_link will be set to 24*827e37b5SSimon Glass the one for the given architecture (or None if not found). 25*827e37b5SSimon Glass 26*827e37b5SSimon Glass Args: 27*827e37b5SSimon Glass arch: Architecture to search for 28*827e37b5SSimon Glass """ 29*827e37b5SSimon Glass HTMLParser.__init__(self) 30*827e37b5SSimon Glass self.arch_link = None 31*827e37b5SSimon Glass self.links = [] 32*827e37b5SSimon Glass self._match = '_%s-' % arch 33*827e37b5SSimon Glass 34*827e37b5SSimon Glass def handle_starttag(self, tag, attrs): 35*827e37b5SSimon Glass if tag == 'a': 36*827e37b5SSimon Glass for tag, value in attrs: 37*827e37b5SSimon Glass if tag == 'href': 38*827e37b5SSimon Glass if value and value.endswith('.xz'): 39*827e37b5SSimon Glass self.links.append(value) 40*827e37b5SSimon Glass if self._match in value: 41*827e37b5SSimon Glass self.arch_link = value 42*827e37b5SSimon Glass 43*827e37b5SSimon 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 60fc3fe1c2SSimon Glass """ 61fc3fe1c2SSimon Glass self.gcc = fname 62fc3fe1c2SSimon Glass self.path = os.path.dirname(fname) 63b5324123SSimon Glass 64b5324123SSimon Glass # Find the CROSS_COMPILE prefix to use for U-Boot. For example, 65b5324123SSimon Glass # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'. 66b5324123SSimon Glass basename = os.path.basename(fname) 67b5324123SSimon Glass pos = basename.rfind('-') 68b5324123SSimon Glass self.cross = basename[:pos + 1] if pos != -1 else '' 69b5324123SSimon Glass 70b5324123SSimon Glass # The architecture is the first part of the name 71fc3fe1c2SSimon Glass pos = self.cross.find('-') 72fc3fe1c2SSimon Glass self.arch = self.cross[:pos] if pos != -1 else 'sandbox' 73fc3fe1c2SSimon Glass 74bb1501f2SSimon Glass env = self.MakeEnvironment(False) 75fc3fe1c2SSimon Glass 76fc3fe1c2SSimon Glass # As a basic sanity check, run the C compiler with --version 77fc3fe1c2SSimon Glass cmd = [fname, '--version'] 78fc3fe1c2SSimon Glass if test: 798bb2bddcSStephen Warren result = command.RunPipe([cmd], capture=True, env=env, 808bb2bddcSStephen Warren raise_on_error=False) 81fc3fe1c2SSimon Glass self.ok = result.return_code == 0 82fc3fe1c2SSimon Glass if verbose: 83fc3fe1c2SSimon Glass print 'Tool chain test: ', 84fc3fe1c2SSimon Glass if self.ok: 85fc3fe1c2SSimon Glass print 'OK' 86fc3fe1c2SSimon Glass else: 87fc3fe1c2SSimon Glass print 'BAD' 88fc3fe1c2SSimon Glass print 'Command: ', cmd 89fc3fe1c2SSimon Glass print result.stdout 90fc3fe1c2SSimon Glass print result.stderr 91fc3fe1c2SSimon Glass else: 92fc3fe1c2SSimon Glass self.ok = True 93fc3fe1c2SSimon Glass self.priority = self.GetPriority(fname) 94fc3fe1c2SSimon Glass 95fc3fe1c2SSimon Glass def GetPriority(self, fname): 96fc3fe1c2SSimon Glass """Return the priority of the toolchain. 97fc3fe1c2SSimon Glass 98fc3fe1c2SSimon Glass Toolchains are ranked according to their suitability by their 99fc3fe1c2SSimon Glass filename prefix. 100fc3fe1c2SSimon Glass 101fc3fe1c2SSimon Glass Args: 102fc3fe1c2SSimon Glass fname: Filename of toolchain 103fc3fe1c2SSimon Glass Returns: 104fc3fe1c2SSimon Glass Priority of toolchain, 0=highest, 20=lowest. 105fc3fe1c2SSimon Glass """ 1068708267fSMasahiro Yamada priority_list = ['-elf', '-unknown-linux-gnu', '-linux', 107fc3fe1c2SSimon Glass '-none-linux-gnueabi', '-uclinux', '-none-eabi', 108fc3fe1c2SSimon Glass '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux'] 109fc3fe1c2SSimon Glass for prio in range(len(priority_list)): 110fc3fe1c2SSimon Glass if priority_list[prio] in fname: 111fc3fe1c2SSimon Glass return prio 112fc3fe1c2SSimon Glass return prio 113fc3fe1c2SSimon Glass 114bb1501f2SSimon Glass def MakeEnvironment(self, full_path): 115fc3fe1c2SSimon Glass """Returns an environment for using the toolchain. 116fc3fe1c2SSimon Glass 117bb1501f2SSimon Glass Thie takes the current environment and adds CROSS_COMPILE so that 118bb1501f2SSimon Glass the tool chain will operate correctly. 119bb1501f2SSimon Glass 120bb1501f2SSimon Glass Args: 121bb1501f2SSimon Glass full_path: Return the full path in CROSS_COMPILE and don't set 122bb1501f2SSimon Glass PATH 123fc3fe1c2SSimon Glass """ 124fc3fe1c2SSimon Glass env = dict(os.environ) 125bb1501f2SSimon Glass if full_path: 126bb1501f2SSimon Glass env['CROSS_COMPILE'] = os.path.join(self.path, self.cross) 127bb1501f2SSimon Glass else: 128fc3fe1c2SSimon Glass env['CROSS_COMPILE'] = self.cross 129f210b587SSimon Glass env['PATH'] = self.path + ':' + env['PATH'] 130bb1501f2SSimon Glass 131fc3fe1c2SSimon Glass return env 132fc3fe1c2SSimon Glass 133fc3fe1c2SSimon Glass 134fc3fe1c2SSimon Glassclass Toolchains: 135fc3fe1c2SSimon Glass """Manage a list of toolchains for building U-Boot 136fc3fe1c2SSimon Glass 137fc3fe1c2SSimon Glass We select one toolchain for each architecture type 138fc3fe1c2SSimon Glass 139fc3fe1c2SSimon Glass Public members: 140fc3fe1c2SSimon Glass toolchains: Dict of Toolchain objects, keyed by architecture name 141fc3fe1c2SSimon Glass paths: List of paths to check for toolchains (may contain wildcards) 142fc3fe1c2SSimon Glass """ 143fc3fe1c2SSimon Glass 144fc3fe1c2SSimon Glass def __init__(self): 145fc3fe1c2SSimon Glass self.toolchains = {} 146fc3fe1c2SSimon Glass self.paths = [] 147d4144e45SSimon Glass self._make_flags = dict(bsettings.GetItems('make-flags')) 148d4144e45SSimon Glass 149*827e37b5SSimon Glass def GetPathList(self): 150*827e37b5SSimon Glass """Get a list of available toolchain paths 151*827e37b5SSimon Glass 152*827e37b5SSimon Glass Returns: 153*827e37b5SSimon Glass List of strings, each a path to a toolchain mentioned in the 154*827e37b5SSimon Glass [toolchain] section of the settings file. 155*827e37b5SSimon Glass """ 1564281ad8eSSimon Glass toolchains = bsettings.GetItems('toolchain') 1574281ad8eSSimon Glass if not toolchains: 1584281ad8eSSimon Glass print ("Warning: No tool chains - please add a [toolchain] section" 1594281ad8eSSimon Glass " to your buildman config file %s. See README for details" % 1601826a18dSMasahiro Yamada bsettings.config_fname) 1614281ad8eSSimon Glass 162*827e37b5SSimon Glass paths = [] 1634281ad8eSSimon Glass for name, value in toolchains: 164fc3fe1c2SSimon Glass if '*' in value: 165*827e37b5SSimon Glass paths += glob.glob(value) 166fc3fe1c2SSimon Glass else: 167*827e37b5SSimon Glass paths.append(value) 168*827e37b5SSimon Glass return paths 169*827e37b5SSimon Glass 170*827e37b5SSimon Glass def GetSettings(self): 171*827e37b5SSimon Glass self.paths += self.GetPathList() 172fc3fe1c2SSimon Glass 173fc3fe1c2SSimon Glass def Add(self, fname, test=True, verbose=False): 174fc3fe1c2SSimon Glass """Add a toolchain to our list 175fc3fe1c2SSimon Glass 176fc3fe1c2SSimon Glass We select the given toolchain as our preferred one for its 177fc3fe1c2SSimon Glass architecture if it is a higher priority than the others. 178fc3fe1c2SSimon Glass 179fc3fe1c2SSimon Glass Args: 180fc3fe1c2SSimon Glass fname: Filename of toolchain's gcc driver 181fc3fe1c2SSimon Glass test: True to run the toolchain to test it 182fc3fe1c2SSimon Glass """ 183fc3fe1c2SSimon Glass toolchain = Toolchain(fname, test, verbose) 184fc3fe1c2SSimon Glass add_it = toolchain.ok 185fc3fe1c2SSimon Glass if toolchain.arch in self.toolchains: 186fc3fe1c2SSimon Glass add_it = (toolchain.priority < 187fc3fe1c2SSimon Glass self.toolchains[toolchain.arch].priority) 188fc3fe1c2SSimon Glass if add_it: 189fc3fe1c2SSimon Glass self.toolchains[toolchain.arch] = toolchain 190fc3fe1c2SSimon Glass 191*827e37b5SSimon Glass def ScanPath(self, path, verbose): 192*827e37b5SSimon Glass """Scan a path for a valid toolchain 193*827e37b5SSimon Glass 194*827e37b5SSimon Glass Args: 195*827e37b5SSimon Glass path: Path to scan 196*827e37b5SSimon Glass verbose: True to print out progress information 197*827e37b5SSimon Glass Returns: 198*827e37b5SSimon Glass Filename of C compiler if found, else None 199*827e37b5SSimon Glass """ 200*827e37b5SSimon Glass for subdir in ['.', 'bin', 'usr/bin']: 201*827e37b5SSimon Glass dirname = os.path.join(path, subdir) 202*827e37b5SSimon Glass if verbose: print " - looking in '%s'" % dirname 203*827e37b5SSimon Glass for fname in glob.glob(dirname + '/*gcc'): 204*827e37b5SSimon Glass if verbose: print " - found '%s'" % fname 205*827e37b5SSimon Glass return fname 206*827e37b5SSimon Glass return None 207*827e37b5SSimon Glass 208*827e37b5SSimon Glass 209fc3fe1c2SSimon Glass def Scan(self, verbose): 210fc3fe1c2SSimon Glass """Scan for available toolchains and select the best for each arch. 211fc3fe1c2SSimon Glass 212fc3fe1c2SSimon Glass We look for all the toolchains we can file, figure out the 213fc3fe1c2SSimon Glass architecture for each, and whether it works. Then we select the 214fc3fe1c2SSimon Glass highest priority toolchain for each arch. 215fc3fe1c2SSimon Glass 216fc3fe1c2SSimon Glass Args: 217fc3fe1c2SSimon Glass verbose: True to print out progress information 218fc3fe1c2SSimon Glass """ 219fc3fe1c2SSimon Glass if verbose: print 'Scanning for tool chains' 220fc3fe1c2SSimon Glass for path in self.paths: 221fc3fe1c2SSimon Glass if verbose: print " - scanning path '%s'" % path 222*827e37b5SSimon Glass fname = self.ScanPath(path, verbose) 223*827e37b5SSimon Glass if fname: 224fc3fe1c2SSimon Glass self.Add(fname, True, verbose) 225fc3fe1c2SSimon Glass 226fc3fe1c2SSimon Glass def List(self): 227fc3fe1c2SSimon Glass """List out the selected toolchains for each architecture""" 228fc3fe1c2SSimon Glass print 'List of available toolchains (%d):' % len(self.toolchains) 229fc3fe1c2SSimon Glass if len(self.toolchains): 230fc3fe1c2SSimon Glass for key, value in sorted(self.toolchains.iteritems()): 231fc3fe1c2SSimon Glass print '%-10s: %s' % (key, value.gcc) 232fc3fe1c2SSimon Glass else: 233fc3fe1c2SSimon Glass print 'None' 234fc3fe1c2SSimon Glass 235fc3fe1c2SSimon Glass def Select(self, arch): 236fc3fe1c2SSimon Glass """Returns the toolchain for a given architecture 237fc3fe1c2SSimon Glass 238fc3fe1c2SSimon Glass Args: 239fc3fe1c2SSimon Glass args: Name of architecture (e.g. 'arm', 'ppc_8xx') 240fc3fe1c2SSimon Glass 241fc3fe1c2SSimon Glass returns: 242fc3fe1c2SSimon Glass toolchain object, or None if none found 243fc3fe1c2SSimon Glass """ 2449b83bfdcSSimon Glass for tag, value in bsettings.GetItems('toolchain-alias'): 2459b83bfdcSSimon Glass if arch == tag: 2469b83bfdcSSimon Glass for alias in value.split(): 2479b83bfdcSSimon Glass if alias in self.toolchains: 2489b83bfdcSSimon Glass return self.toolchains[alias] 249fc3fe1c2SSimon Glass 250fc3fe1c2SSimon Glass if not arch in self.toolchains: 251fc3fe1c2SSimon Glass raise ValueError, ("No tool chain found for arch '%s'" % arch) 252fc3fe1c2SSimon Glass return self.toolchains[arch] 2534281ad8eSSimon Glass 2544281ad8eSSimon Glass def ResolveReferences(self, var_dict, args): 2554281ad8eSSimon Glass """Resolve variable references in a string 2564281ad8eSSimon Glass 2574281ad8eSSimon Glass This converts ${blah} within the string to the value of blah. 2584281ad8eSSimon Glass This function works recursively. 2594281ad8eSSimon Glass 2604281ad8eSSimon Glass Args: 2614281ad8eSSimon Glass var_dict: Dictionary containing variables and their values 2624281ad8eSSimon Glass args: String containing make arguments 2634281ad8eSSimon Glass Returns: 2644281ad8eSSimon Glass Resolved string 2654281ad8eSSimon Glass 2664281ad8eSSimon Glass >>> bsettings.Setup() 2674281ad8eSSimon Glass >>> tcs = Toolchains() 2684281ad8eSSimon Glass >>> tcs.Add('fred', False) 2694281ad8eSSimon Glass >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ 2704281ad8eSSimon Glass 'second' : '2nd'} 2714281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') 2724281ad8eSSimon Glass 'this=OBLIQUE_set' 2734281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') 2744281ad8eSSimon Glass 'this=OBLIQUE_setfi2ndrstnd' 2754281ad8eSSimon Glass """ 276f60c9d4fSSimon Glass re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') 2774281ad8eSSimon Glass 2784281ad8eSSimon Glass while True: 2794281ad8eSSimon Glass m = re_var.search(args) 2804281ad8eSSimon Glass if not m: 2814281ad8eSSimon Glass break 2824281ad8eSSimon Glass lookup = m.group(0)[2:-1] 2834281ad8eSSimon Glass value = var_dict.get(lookup, '') 2844281ad8eSSimon Glass args = args[:m.start(0)] + value + args[m.end(0):] 2854281ad8eSSimon Glass return args 2864281ad8eSSimon Glass 2874281ad8eSSimon Glass def GetMakeArguments(self, board): 2884281ad8eSSimon Glass """Returns 'make' arguments for a given board 2894281ad8eSSimon Glass 2904281ad8eSSimon Glass The flags are in a section called 'make-flags'. Flags are named 2914281ad8eSSimon Glass after the target they represent, for example snapper9260=TESTING=1 2924281ad8eSSimon Glass will pass TESTING=1 to make when building the snapper9260 board. 2934281ad8eSSimon Glass 2944281ad8eSSimon Glass References to other boards can be added in the string also. For 2954281ad8eSSimon Glass example: 2964281ad8eSSimon Glass 2974281ad8eSSimon Glass [make-flags] 2984281ad8eSSimon Glass at91-boards=ENABLE_AT91_TEST=1 2994281ad8eSSimon Glass snapper9260=${at91-boards} BUILD_TAG=442 3004281ad8eSSimon Glass snapper9g45=${at91-boards} BUILD_TAG=443 3014281ad8eSSimon Glass 3024281ad8eSSimon Glass This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 3034281ad8eSSimon Glass and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. 3044281ad8eSSimon Glass 3054281ad8eSSimon Glass A special 'target' variable is set to the board target. 3064281ad8eSSimon Glass 3074281ad8eSSimon Glass Args: 3084281ad8eSSimon Glass board: Board object for the board to check. 3094281ad8eSSimon Glass Returns: 3104281ad8eSSimon Glass 'make' flags for that board, or '' if none 3114281ad8eSSimon Glass """ 3124281ad8eSSimon Glass self._make_flags['target'] = board.target 3134281ad8eSSimon Glass arg_str = self.ResolveReferences(self._make_flags, 3144281ad8eSSimon Glass self._make_flags.get(board.target, '')) 3154281ad8eSSimon Glass args = arg_str.split(' ') 3164281ad8eSSimon Glass i = 0 3174281ad8eSSimon Glass while i < len(args): 3184281ad8eSSimon Glass if not args[i]: 3194281ad8eSSimon Glass del args[i] 3204281ad8eSSimon Glass else: 3214281ad8eSSimon Glass i += 1 3224281ad8eSSimon Glass return args 323*827e37b5SSimon Glass 324*827e37b5SSimon Glass def LocateArchUrl(self, fetch_arch): 325*827e37b5SSimon Glass """Find a toolchain available online 326*827e37b5SSimon Glass 327*827e37b5SSimon Glass Look in standard places for available toolchains. At present the 328*827e37b5SSimon Glass only standard place is at kernel.org. 329*827e37b5SSimon Glass 330*827e37b5SSimon Glass Args: 331*827e37b5SSimon Glass arch: Architecture to look for, or 'list' for all 332*827e37b5SSimon Glass Returns: 333*827e37b5SSimon Glass If fetch_arch is 'list', a tuple: 334*827e37b5SSimon Glass Machine architecture (e.g. x86_64) 335*827e37b5SSimon Glass List of toolchains 336*827e37b5SSimon Glass else 337*827e37b5SSimon Glass URL containing this toolchain, if avaialble, else None 338*827e37b5SSimon Glass """ 339*827e37b5SSimon Glass arch = command.OutputOneLine('uname', '-m') 340*827e37b5SSimon Glass base = 'https://www.kernel.org/pub/tools/crosstool/files/bin' 341*827e37b5SSimon Glass versions = ['4.6.3', '4.6.2', '4.5.1', '4.2.4'] 342*827e37b5SSimon Glass links = [] 343*827e37b5SSimon Glass for version in versions: 344*827e37b5SSimon Glass url = '%s/%s/%s/' % (base, arch, version) 345*827e37b5SSimon Glass print 'Checking: %s' % url 346*827e37b5SSimon Glass response = urllib2.urlopen(url) 347*827e37b5SSimon Glass html = response.read() 348*827e37b5SSimon Glass parser = MyHTMLParser(fetch_arch) 349*827e37b5SSimon Glass parser.feed(html) 350*827e37b5SSimon Glass if fetch_arch == 'list': 351*827e37b5SSimon Glass links += parser.links 352*827e37b5SSimon Glass elif parser.arch_link: 353*827e37b5SSimon Glass return url + parser.arch_link 354*827e37b5SSimon Glass if fetch_arch == 'list': 355*827e37b5SSimon Glass return arch, links 356*827e37b5SSimon Glass return None 357*827e37b5SSimon Glass 358*827e37b5SSimon Glass def Download(self, url): 359*827e37b5SSimon Glass """Download a file to a temporary directory 360*827e37b5SSimon Glass 361*827e37b5SSimon Glass Args: 362*827e37b5SSimon Glass url: URL to download 363*827e37b5SSimon Glass Returns: 364*827e37b5SSimon Glass Tuple: 365*827e37b5SSimon Glass Temporary directory name 366*827e37b5SSimon Glass Full path to the downloaded archive file in that directory, 367*827e37b5SSimon Glass or None if there was an error while downloading 368*827e37b5SSimon Glass """ 369*827e37b5SSimon Glass print "Downloading: %s" % url 370*827e37b5SSimon Glass leaf = url.split('/')[-1] 371*827e37b5SSimon Glass tmpdir = tempfile.mkdtemp('.buildman') 372*827e37b5SSimon Glass response = urllib2.urlopen(url) 373*827e37b5SSimon Glass fname = os.path.join(tmpdir, leaf) 374*827e37b5SSimon Glass fd = open(fname, 'wb') 375*827e37b5SSimon Glass meta = response.info() 376*827e37b5SSimon Glass size = int(meta.getheaders("Content-Length")[0]) 377*827e37b5SSimon Glass done = 0 378*827e37b5SSimon Glass block_size = 1 << 16 379*827e37b5SSimon Glass status = '' 380*827e37b5SSimon Glass 381*827e37b5SSimon Glass # Read the file in chunks and show progress as we go 382*827e37b5SSimon Glass while True: 383*827e37b5SSimon Glass buffer = response.read(block_size) 384*827e37b5SSimon Glass if not buffer: 385*827e37b5SSimon Glass print chr(8) * (len(status) + 1), '\r', 386*827e37b5SSimon Glass break 387*827e37b5SSimon Glass 388*827e37b5SSimon Glass done += len(buffer) 389*827e37b5SSimon Glass fd.write(buffer) 390*827e37b5SSimon Glass status = r"%10d MiB [%3d%%]" % (done / 1024 / 1024, 391*827e37b5SSimon Glass done * 100 / size) 392*827e37b5SSimon Glass status = status + chr(8) * (len(status) + 1) 393*827e37b5SSimon Glass print status, 394*827e37b5SSimon Glass sys.stdout.flush() 395*827e37b5SSimon Glass fd.close() 396*827e37b5SSimon Glass if done != size: 397*827e37b5SSimon Glass print 'Error, failed to download' 398*827e37b5SSimon Glass os.remove(fname) 399*827e37b5SSimon Glass fname = None 400*827e37b5SSimon Glass return tmpdir, fname 401*827e37b5SSimon Glass 402*827e37b5SSimon Glass def Unpack(self, fname, dest): 403*827e37b5SSimon Glass """Unpack a tar file 404*827e37b5SSimon Glass 405*827e37b5SSimon Glass Args: 406*827e37b5SSimon Glass fname: Filename to unpack 407*827e37b5SSimon Glass dest: Destination directory 408*827e37b5SSimon Glass Returns: 409*827e37b5SSimon Glass Directory name of the first entry in the archive, without the 410*827e37b5SSimon Glass trailing / 411*827e37b5SSimon Glass """ 412*827e37b5SSimon Glass stdout = command.Output('tar', 'xvfJ', fname, '-C', dest) 413*827e37b5SSimon Glass return stdout.splitlines()[0][:-1] 414*827e37b5SSimon Glass 415*827e37b5SSimon Glass def TestSettingsHasPath(self, path): 416*827e37b5SSimon Glass """Check if builmand will find this toolchain 417*827e37b5SSimon Glass 418*827e37b5SSimon Glass Returns: 419*827e37b5SSimon Glass True if the path is in settings, False if not 420*827e37b5SSimon Glass """ 421*827e37b5SSimon Glass paths = self.GetPathList() 422*827e37b5SSimon Glass return path in paths 423*827e37b5SSimon Glass 424*827e37b5SSimon Glass def ListArchs(self): 425*827e37b5SSimon Glass """List architectures with available toolchains to download""" 426*827e37b5SSimon Glass host_arch, archives = self.LocateArchUrl('list') 427*827e37b5SSimon Glass re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*') 428*827e37b5SSimon Glass arch_set = set() 429*827e37b5SSimon Glass for archive in archives: 430*827e37b5SSimon Glass # Remove the host architecture from the start 431*827e37b5SSimon Glass arch = re_arch.match(archive[len(host_arch):]) 432*827e37b5SSimon Glass if arch: 433*827e37b5SSimon Glass arch_set.add(arch.group(1)) 434*827e37b5SSimon Glass return sorted(arch_set) 435*827e37b5SSimon Glass 436*827e37b5SSimon Glass def FetchAndInstall(self, arch): 437*827e37b5SSimon Glass """Fetch and install a new toolchain 438*827e37b5SSimon Glass 439*827e37b5SSimon Glass arch: 440*827e37b5SSimon Glass Architecture to fetch, or 'list' to list 441*827e37b5SSimon Glass """ 442*827e37b5SSimon Glass # Fist get the URL for this architecture 443*827e37b5SSimon Glass url = self.LocateArchUrl(arch) 444*827e37b5SSimon Glass if not url: 445*827e37b5SSimon Glass print ("Cannot find toolchain for arch '%s' - use 'list' to list" % 446*827e37b5SSimon Glass arch) 447*827e37b5SSimon Glass return 2 448*827e37b5SSimon Glass home = os.environ['HOME'] 449*827e37b5SSimon Glass dest = os.path.join(home, '.buildman-toolchains') 450*827e37b5SSimon Glass if not os.path.exists(dest): 451*827e37b5SSimon Glass os.mkdir(dest) 452*827e37b5SSimon Glass 453*827e37b5SSimon Glass # Download the tar file for this toolchain and unpack it 454*827e37b5SSimon Glass tmpdir, tarfile = self.Download(url) 455*827e37b5SSimon Glass if not tarfile: 456*827e37b5SSimon Glass return 1 457*827e37b5SSimon Glass print 'Unpacking to: %s' % dest, 458*827e37b5SSimon Glass sys.stdout.flush() 459*827e37b5SSimon Glass path = self.Unpack(tarfile, dest) 460*827e37b5SSimon Glass os.remove(tarfile) 461*827e37b5SSimon Glass os.rmdir(tmpdir) 462*827e37b5SSimon Glass print 463*827e37b5SSimon Glass 464*827e37b5SSimon Glass # Check that the toolchain works 465*827e37b5SSimon Glass print 'Testing' 466*827e37b5SSimon Glass dirpath = os.path.join(dest, path) 467*827e37b5SSimon Glass compiler_fname = self.ScanPath(dirpath, True) 468*827e37b5SSimon Glass if not compiler_fname: 469*827e37b5SSimon Glass print 'Could not locate C compiler - fetch failed.' 470*827e37b5SSimon Glass return 1 471*827e37b5SSimon Glass toolchain = Toolchain(compiler_fname, True, True) 472*827e37b5SSimon Glass 473*827e37b5SSimon Glass # Make sure that it will be found by buildman 474*827e37b5SSimon Glass if not self.TestSettingsHasPath(dirpath): 475*827e37b5SSimon Glass print ("Adding 'download' to config file '%s'" % 476*827e37b5SSimon Glass bsettings.config_fname) 477*827e37b5SSimon Glass tools_dir = os.path.dirname(dirpath) 478*827e37b5SSimon Glass bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir) 479*827e37b5SSimon Glass return 0 480