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 17*17bce66cSSimon Glass(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH, 18*17bce66cSSimon Glass PRIORITY_CALC) = range(4) 19ff690df9SSimon Glass 20827e37b5SSimon Glass# Simple class to collect links from a page 21827e37b5SSimon Glassclass MyHTMLParser(HTMLParser): 22827e37b5SSimon Glass def __init__(self, arch): 23827e37b5SSimon Glass """Create a new parser 24827e37b5SSimon Glass 25827e37b5SSimon Glass After the parser runs, self.links will be set to a list of the links 26827e37b5SSimon Glass to .xz archives found in the page, and self.arch_link will be set to 27827e37b5SSimon Glass the one for the given architecture (or None if not found). 28827e37b5SSimon Glass 29827e37b5SSimon Glass Args: 30827e37b5SSimon Glass arch: Architecture to search for 31827e37b5SSimon Glass """ 32827e37b5SSimon Glass HTMLParser.__init__(self) 33827e37b5SSimon Glass self.arch_link = None 34827e37b5SSimon Glass self.links = [] 35827e37b5SSimon Glass self._match = '_%s-' % arch 36827e37b5SSimon Glass 37827e37b5SSimon Glass def handle_starttag(self, tag, attrs): 38827e37b5SSimon Glass if tag == 'a': 39827e37b5SSimon Glass for tag, value in attrs: 40827e37b5SSimon Glass if tag == 'href': 41827e37b5SSimon Glass if value and value.endswith('.xz'): 42827e37b5SSimon Glass self.links.append(value) 43827e37b5SSimon Glass if self._match in value: 44827e37b5SSimon Glass self.arch_link = value 45827e37b5SSimon Glass 46827e37b5SSimon Glass 47fc3fe1c2SSimon Glassclass Toolchain: 48fc3fe1c2SSimon Glass """A single toolchain 49fc3fe1c2SSimon Glass 50fc3fe1c2SSimon Glass Public members: 51fc3fe1c2SSimon Glass gcc: Full path to C compiler 52fc3fe1c2SSimon Glass path: Directory path containing C compiler 53fc3fe1c2SSimon Glass cross: Cross compile string, e.g. 'arm-linux-' 54fc3fe1c2SSimon Glass arch: Architecture of toolchain as determined from the first 55fc3fe1c2SSimon Glass component of the filename. E.g. arm-linux-gcc becomes arm 56ff690df9SSimon Glass priority: Toolchain priority (0=highest, 20=lowest) 57fc3fe1c2SSimon Glass """ 58608e399fSSimon Glass def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC, 59608e399fSSimon Glass arch=None): 60fc3fe1c2SSimon Glass """Create a new toolchain object. 61fc3fe1c2SSimon Glass 62fc3fe1c2SSimon Glass Args: 63fc3fe1c2SSimon Glass fname: Filename of the gcc component 64fc3fe1c2SSimon Glass test: True to run the toolchain to test it 65ad24ebacSSimon Glass verbose: True to print out the information 66ff690df9SSimon Glass priority: Priority to use for this toolchain, or PRIORITY_CALC to 67ff690df9SSimon Glass calculate it 68fc3fe1c2SSimon Glass """ 69fc3fe1c2SSimon Glass self.gcc = fname 70fc3fe1c2SSimon Glass self.path = os.path.dirname(fname) 71b5324123SSimon Glass 72b5324123SSimon Glass # Find the CROSS_COMPILE prefix to use for U-Boot. For example, 73b5324123SSimon Glass # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'. 74b5324123SSimon Glass basename = os.path.basename(fname) 75b5324123SSimon Glass pos = basename.rfind('-') 76b5324123SSimon Glass self.cross = basename[:pos + 1] if pos != -1 else '' 77b5324123SSimon Glass 78b5324123SSimon Glass # The architecture is the first part of the name 79fc3fe1c2SSimon Glass pos = self.cross.find('-') 80608e399fSSimon Glass if arch: 81608e399fSSimon Glass self.arch = arch 82608e399fSSimon Glass else: 83fc3fe1c2SSimon Glass self.arch = self.cross[:pos] if pos != -1 else 'sandbox' 84fc3fe1c2SSimon Glass 85bb1501f2SSimon Glass env = self.MakeEnvironment(False) 86fc3fe1c2SSimon Glass 87fc3fe1c2SSimon Glass # As a basic sanity check, run the C compiler with --version 88fc3fe1c2SSimon Glass cmd = [fname, '--version'] 89ff690df9SSimon Glass if priority == PRIORITY_CALC: 90ff690df9SSimon Glass self.priority = self.GetPriority(fname) 91ff690df9SSimon Glass else: 92ff690df9SSimon Glass self.priority = priority 93fc3fe1c2SSimon Glass if test: 948bb2bddcSStephen Warren result = command.RunPipe([cmd], capture=True, env=env, 958bb2bddcSStephen Warren raise_on_error=False) 96fc3fe1c2SSimon Glass self.ok = result.return_code == 0 97fc3fe1c2SSimon Glass if verbose: 98fc3fe1c2SSimon Glass print 'Tool chain test: ', 99fc3fe1c2SSimon Glass if self.ok: 100608e399fSSimon Glass print "OK, arch='%s', priority %d" % (self.arch, 101608e399fSSimon Glass self.priority) 102fc3fe1c2SSimon Glass else: 103fc3fe1c2SSimon Glass print 'BAD' 104fc3fe1c2SSimon Glass print 'Command: ', cmd 105fc3fe1c2SSimon Glass print result.stdout 106fc3fe1c2SSimon Glass print result.stderr 107fc3fe1c2SSimon Glass else: 108fc3fe1c2SSimon Glass self.ok = True 109fc3fe1c2SSimon Glass 110fc3fe1c2SSimon Glass def GetPriority(self, fname): 111fc3fe1c2SSimon Glass """Return the priority of the toolchain. 112fc3fe1c2SSimon Glass 113fc3fe1c2SSimon Glass Toolchains are ranked according to their suitability by their 114fc3fe1c2SSimon Glass filename prefix. 115fc3fe1c2SSimon Glass 116fc3fe1c2SSimon Glass Args: 117fc3fe1c2SSimon Glass fname: Filename of toolchain 118fc3fe1c2SSimon Glass Returns: 119ff690df9SSimon Glass Priority of toolchain, PRIORITY_CALC=highest, 20=lowest. 120fc3fe1c2SSimon Glass """ 1218708267fSMasahiro Yamada priority_list = ['-elf', '-unknown-linux-gnu', '-linux', 122fc3fe1c2SSimon Glass '-none-linux-gnueabi', '-uclinux', '-none-eabi', 123fc3fe1c2SSimon Glass '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux'] 124fc3fe1c2SSimon Glass for prio in range(len(priority_list)): 125fc3fe1c2SSimon Glass if priority_list[prio] in fname: 126ff690df9SSimon Glass return PRIORITY_CALC + prio 127ff690df9SSimon Glass return PRIORITY_CALC + prio 128fc3fe1c2SSimon Glass 129bb1501f2SSimon Glass def MakeEnvironment(self, full_path): 130fc3fe1c2SSimon Glass """Returns an environment for using the toolchain. 131fc3fe1c2SSimon Glass 132bb1501f2SSimon Glass Thie takes the current environment and adds CROSS_COMPILE so that 133bb1501f2SSimon Glass the tool chain will operate correctly. 134bb1501f2SSimon Glass 135bb1501f2SSimon Glass Args: 136bb1501f2SSimon Glass full_path: Return the full path in CROSS_COMPILE and don't set 137bb1501f2SSimon Glass PATH 138fc3fe1c2SSimon Glass """ 139fc3fe1c2SSimon Glass env = dict(os.environ) 140bb1501f2SSimon Glass if full_path: 141bb1501f2SSimon Glass env['CROSS_COMPILE'] = os.path.join(self.path, self.cross) 142bb1501f2SSimon Glass else: 143fc3fe1c2SSimon Glass env['CROSS_COMPILE'] = self.cross 144f210b587SSimon Glass env['PATH'] = self.path + ':' + env['PATH'] 145bb1501f2SSimon Glass 146fc3fe1c2SSimon Glass return env 147fc3fe1c2SSimon Glass 148fc3fe1c2SSimon Glass 149fc3fe1c2SSimon Glassclass Toolchains: 150fc3fe1c2SSimon Glass """Manage a list of toolchains for building U-Boot 151fc3fe1c2SSimon Glass 152fc3fe1c2SSimon Glass We select one toolchain for each architecture type 153fc3fe1c2SSimon Glass 154fc3fe1c2SSimon Glass Public members: 155fc3fe1c2SSimon Glass toolchains: Dict of Toolchain objects, keyed by architecture name 156*17bce66cSSimon Glass prefixes: Dict of prefixes to check, keyed by architecture. This can 157*17bce66cSSimon Glass be a full path and toolchain prefix, for example 158*17bce66cSSimon Glass {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of 159*17bce66cSSimon Glass something on the search path, for example 160*17bce66cSSimon Glass {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported. 161fc3fe1c2SSimon Glass paths: List of paths to check for toolchains (may contain wildcards) 162fc3fe1c2SSimon Glass """ 163fc3fe1c2SSimon Glass 164fc3fe1c2SSimon Glass def __init__(self): 165fc3fe1c2SSimon Glass self.toolchains = {} 166*17bce66cSSimon Glass self.prefixes = {} 167fc3fe1c2SSimon Glass self.paths = [] 168d4144e45SSimon Glass self._make_flags = dict(bsettings.GetItems('make-flags')) 169d4144e45SSimon Glass 170827e37b5SSimon Glass def GetPathList(self): 171827e37b5SSimon Glass """Get a list of available toolchain paths 172827e37b5SSimon Glass 173827e37b5SSimon Glass Returns: 174827e37b5SSimon Glass List of strings, each a path to a toolchain mentioned in the 175827e37b5SSimon Glass [toolchain] section of the settings file. 176827e37b5SSimon Glass """ 1774281ad8eSSimon Glass toolchains = bsettings.GetItems('toolchain') 1784281ad8eSSimon Glass if not toolchains: 179ad24ebacSSimon Glass print ('Warning: No tool chains - please add a [toolchain] section' 180ad24ebacSSimon Glass ' to your buildman config file %s. See README for details' % 1811826a18dSMasahiro Yamada bsettings.config_fname) 1824281ad8eSSimon Glass 183827e37b5SSimon Glass paths = [] 1844281ad8eSSimon Glass for name, value in toolchains: 185fc3fe1c2SSimon Glass if '*' in value: 186827e37b5SSimon Glass paths += glob.glob(value) 187fc3fe1c2SSimon Glass else: 188827e37b5SSimon Glass paths.append(value) 189827e37b5SSimon Glass return paths 190827e37b5SSimon Glass 191827e37b5SSimon Glass def GetSettings(self): 192*17bce66cSSimon Glass self.prefixes = bsettings.GetItems('toolchain-prefix') 193827e37b5SSimon Glass self.paths += self.GetPathList() 194fc3fe1c2SSimon Glass 195608e399fSSimon Glass def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC, 196608e399fSSimon Glass arch=None): 197fc3fe1c2SSimon Glass """Add a toolchain to our list 198fc3fe1c2SSimon Glass 199fc3fe1c2SSimon Glass We select the given toolchain as our preferred one for its 200fc3fe1c2SSimon Glass architecture if it is a higher priority than the others. 201fc3fe1c2SSimon Glass 202fc3fe1c2SSimon Glass Args: 203fc3fe1c2SSimon Glass fname: Filename of toolchain's gcc driver 204fc3fe1c2SSimon Glass test: True to run the toolchain to test it 205ff690df9SSimon Glass priority: Priority to use for this toolchain 206608e399fSSimon Glass arch: Toolchain architecture, or None if not known 207fc3fe1c2SSimon Glass """ 208608e399fSSimon Glass toolchain = Toolchain(fname, test, verbose, priority, arch) 209fc3fe1c2SSimon Glass add_it = toolchain.ok 210fc3fe1c2SSimon Glass if toolchain.arch in self.toolchains: 211fc3fe1c2SSimon Glass add_it = (toolchain.priority < 212fc3fe1c2SSimon Glass self.toolchains[toolchain.arch].priority) 213fc3fe1c2SSimon Glass if add_it: 214fc3fe1c2SSimon Glass self.toolchains[toolchain.arch] = toolchain 215ff690df9SSimon Glass elif verbose: 216ff690df9SSimon Glass print ("Toolchain '%s' at priority %d will be ignored because " 217ff690df9SSimon Glass "another toolchain for arch '%s' has priority %d" % 218ff690df9SSimon Glass (toolchain.gcc, toolchain.priority, toolchain.arch, 219ff690df9SSimon Glass self.toolchains[toolchain.arch].priority)) 220fc3fe1c2SSimon Glass 221827e37b5SSimon Glass def ScanPath(self, path, verbose): 222827e37b5SSimon Glass """Scan a path for a valid toolchain 223827e37b5SSimon Glass 224827e37b5SSimon Glass Args: 225827e37b5SSimon Glass path: Path to scan 226827e37b5SSimon Glass verbose: True to print out progress information 227827e37b5SSimon Glass Returns: 228827e37b5SSimon Glass Filename of C compiler if found, else None 229827e37b5SSimon Glass """ 230d9088983SAlbert ARIBAUD fnames = [] 231827e37b5SSimon Glass for subdir in ['.', 'bin', 'usr/bin']: 232827e37b5SSimon Glass dirname = os.path.join(path, subdir) 233827e37b5SSimon Glass if verbose: print " - looking in '%s'" % dirname 234827e37b5SSimon Glass for fname in glob.glob(dirname + '/*gcc'): 235827e37b5SSimon Glass if verbose: print " - found '%s'" % fname 236d9088983SAlbert ARIBAUD fnames.append(fname) 237d9088983SAlbert ARIBAUD return fnames 238827e37b5SSimon Glass 239*17bce66cSSimon Glass def ScanPathEnv(self, fname): 240*17bce66cSSimon Glass """Scan the PATH environment variable for a given filename. 241*17bce66cSSimon Glass 242*17bce66cSSimon Glass Args: 243*17bce66cSSimon Glass fname: Filename to scan for 244*17bce66cSSimon Glass Returns: 245*17bce66cSSimon Glass List of matching pathanames, or [] if none 246*17bce66cSSimon Glass """ 247*17bce66cSSimon Glass pathname_list = [] 248*17bce66cSSimon Glass for path in os.environ["PATH"].split(os.pathsep): 249*17bce66cSSimon Glass path = path.strip('"') 250*17bce66cSSimon Glass pathname = os.path.join(path, fname) 251*17bce66cSSimon Glass if os.path.exists(pathname): 252*17bce66cSSimon Glass pathname_list.append(pathname) 253*17bce66cSSimon Glass return pathname_list 254827e37b5SSimon Glass 255fc3fe1c2SSimon Glass def Scan(self, verbose): 256fc3fe1c2SSimon Glass """Scan for available toolchains and select the best for each arch. 257fc3fe1c2SSimon Glass 258fc3fe1c2SSimon Glass We look for all the toolchains we can file, figure out the 259fc3fe1c2SSimon Glass architecture for each, and whether it works. Then we select the 260fc3fe1c2SSimon Glass highest priority toolchain for each arch. 261fc3fe1c2SSimon Glass 262fc3fe1c2SSimon Glass Args: 263fc3fe1c2SSimon Glass verbose: True to print out progress information 264fc3fe1c2SSimon Glass """ 265fc3fe1c2SSimon Glass if verbose: print 'Scanning for tool chains' 266*17bce66cSSimon Glass for name, value in self.prefixes: 267*17bce66cSSimon Glass if verbose: print " - scanning prefix '%s'" % value 268*17bce66cSSimon Glass if os.path.exists(value): 269*17bce66cSSimon Glass self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name) 270*17bce66cSSimon Glass continue 271*17bce66cSSimon Glass fname = value + 'gcc' 272*17bce66cSSimon Glass if os.path.exists(fname): 273*17bce66cSSimon Glass self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name) 274*17bce66cSSimon Glass continue 275*17bce66cSSimon Glass fname_list = self.ScanPathEnv(fname) 276*17bce66cSSimon Glass for f in fname_list: 277*17bce66cSSimon Glass self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name) 278*17bce66cSSimon Glass if not fname_list: 279*17bce66cSSimon Glass raise ValueError, ("No tool chain found for prefix '%s'" % 280*17bce66cSSimon Glass value) 281fc3fe1c2SSimon Glass for path in self.paths: 282fc3fe1c2SSimon Glass if verbose: print " - scanning path '%s'" % path 283d9088983SAlbert ARIBAUD fnames = self.ScanPath(path, verbose) 284d9088983SAlbert ARIBAUD for fname in fnames: 285fc3fe1c2SSimon Glass self.Add(fname, True, verbose) 286fc3fe1c2SSimon Glass 287fc3fe1c2SSimon Glass def List(self): 288fc3fe1c2SSimon Glass """List out the selected toolchains for each architecture""" 289fc3fe1c2SSimon Glass print 'List of available toolchains (%d):' % len(self.toolchains) 290fc3fe1c2SSimon Glass if len(self.toolchains): 291fc3fe1c2SSimon Glass for key, value in sorted(self.toolchains.iteritems()): 292fc3fe1c2SSimon Glass print '%-10s: %s' % (key, value.gcc) 293fc3fe1c2SSimon Glass else: 294fc3fe1c2SSimon Glass print 'None' 295fc3fe1c2SSimon Glass 296fc3fe1c2SSimon Glass def Select(self, arch): 297fc3fe1c2SSimon Glass """Returns the toolchain for a given architecture 298fc3fe1c2SSimon Glass 299fc3fe1c2SSimon Glass Args: 300fc3fe1c2SSimon Glass args: Name of architecture (e.g. 'arm', 'ppc_8xx') 301fc3fe1c2SSimon Glass 302fc3fe1c2SSimon Glass returns: 303fc3fe1c2SSimon Glass toolchain object, or None if none found 304fc3fe1c2SSimon Glass """ 3059b83bfdcSSimon Glass for tag, value in bsettings.GetItems('toolchain-alias'): 3069b83bfdcSSimon Glass if arch == tag: 3079b83bfdcSSimon Glass for alias in value.split(): 3089b83bfdcSSimon Glass if alias in self.toolchains: 3099b83bfdcSSimon Glass return self.toolchains[alias] 310fc3fe1c2SSimon Glass 311fc3fe1c2SSimon Glass if not arch in self.toolchains: 312fc3fe1c2SSimon Glass raise ValueError, ("No tool chain found for arch '%s'" % arch) 313fc3fe1c2SSimon Glass return self.toolchains[arch] 3144281ad8eSSimon Glass 3154281ad8eSSimon Glass def ResolveReferences(self, var_dict, args): 3164281ad8eSSimon Glass """Resolve variable references in a string 3174281ad8eSSimon Glass 3184281ad8eSSimon Glass This converts ${blah} within the string to the value of blah. 3194281ad8eSSimon Glass This function works recursively. 3204281ad8eSSimon Glass 3214281ad8eSSimon Glass Args: 3224281ad8eSSimon Glass var_dict: Dictionary containing variables and their values 3234281ad8eSSimon Glass args: String containing make arguments 3244281ad8eSSimon Glass Returns: 3254281ad8eSSimon Glass Resolved string 3264281ad8eSSimon Glass 3274281ad8eSSimon Glass >>> bsettings.Setup() 3284281ad8eSSimon Glass >>> tcs = Toolchains() 3294281ad8eSSimon Glass >>> tcs.Add('fred', False) 3304281ad8eSSimon Glass >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ 3314281ad8eSSimon Glass 'second' : '2nd'} 3324281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') 3334281ad8eSSimon Glass 'this=OBLIQUE_set' 3344281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') 3354281ad8eSSimon Glass 'this=OBLIQUE_setfi2ndrstnd' 3364281ad8eSSimon Glass """ 337f60c9d4fSSimon Glass re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') 3384281ad8eSSimon Glass 3394281ad8eSSimon Glass while True: 3404281ad8eSSimon Glass m = re_var.search(args) 3414281ad8eSSimon Glass if not m: 3424281ad8eSSimon Glass break 3434281ad8eSSimon Glass lookup = m.group(0)[2:-1] 3444281ad8eSSimon Glass value = var_dict.get(lookup, '') 3454281ad8eSSimon Glass args = args[:m.start(0)] + value + args[m.end(0):] 3464281ad8eSSimon Glass return args 3474281ad8eSSimon Glass 3484281ad8eSSimon Glass def GetMakeArguments(self, board): 3494281ad8eSSimon Glass """Returns 'make' arguments for a given board 3504281ad8eSSimon Glass 3514281ad8eSSimon Glass The flags are in a section called 'make-flags'. Flags are named 3524281ad8eSSimon Glass after the target they represent, for example snapper9260=TESTING=1 3534281ad8eSSimon Glass will pass TESTING=1 to make when building the snapper9260 board. 3544281ad8eSSimon Glass 3554281ad8eSSimon Glass References to other boards can be added in the string also. For 3564281ad8eSSimon Glass example: 3574281ad8eSSimon Glass 3584281ad8eSSimon Glass [make-flags] 3594281ad8eSSimon Glass at91-boards=ENABLE_AT91_TEST=1 3604281ad8eSSimon Glass snapper9260=${at91-boards} BUILD_TAG=442 3614281ad8eSSimon Glass snapper9g45=${at91-boards} BUILD_TAG=443 3624281ad8eSSimon Glass 3634281ad8eSSimon Glass This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 3644281ad8eSSimon Glass and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. 3654281ad8eSSimon Glass 3664281ad8eSSimon Glass A special 'target' variable is set to the board target. 3674281ad8eSSimon Glass 3684281ad8eSSimon Glass Args: 3694281ad8eSSimon Glass board: Board object for the board to check. 3704281ad8eSSimon Glass Returns: 3714281ad8eSSimon Glass 'make' flags for that board, or '' if none 3724281ad8eSSimon Glass """ 3734281ad8eSSimon Glass self._make_flags['target'] = board.target 3744281ad8eSSimon Glass arg_str = self.ResolveReferences(self._make_flags, 3754281ad8eSSimon Glass self._make_flags.get(board.target, '')) 3764281ad8eSSimon Glass args = arg_str.split(' ') 3774281ad8eSSimon Glass i = 0 3784281ad8eSSimon Glass while i < len(args): 3794281ad8eSSimon Glass if not args[i]: 3804281ad8eSSimon Glass del args[i] 3814281ad8eSSimon Glass else: 3824281ad8eSSimon Glass i += 1 3834281ad8eSSimon Glass return args 384827e37b5SSimon Glass 385827e37b5SSimon Glass def LocateArchUrl(self, fetch_arch): 386827e37b5SSimon Glass """Find a toolchain available online 387827e37b5SSimon Glass 388827e37b5SSimon Glass Look in standard places for available toolchains. At present the 389827e37b5SSimon Glass only standard place is at kernel.org. 390827e37b5SSimon Glass 391827e37b5SSimon Glass Args: 392827e37b5SSimon Glass arch: Architecture to look for, or 'list' for all 393827e37b5SSimon Glass Returns: 394827e37b5SSimon Glass If fetch_arch is 'list', a tuple: 395827e37b5SSimon Glass Machine architecture (e.g. x86_64) 396827e37b5SSimon Glass List of toolchains 397827e37b5SSimon Glass else 398827e37b5SSimon Glass URL containing this toolchain, if avaialble, else None 399827e37b5SSimon Glass """ 400827e37b5SSimon Glass arch = command.OutputOneLine('uname', '-m') 401827e37b5SSimon Glass base = 'https://www.kernel.org/pub/tools/crosstool/files/bin' 4021246231cSMichal Simek versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4'] 403827e37b5SSimon Glass links = [] 404827e37b5SSimon Glass for version in versions: 405827e37b5SSimon Glass url = '%s/%s/%s/' % (base, arch, version) 406827e37b5SSimon Glass print 'Checking: %s' % url 407827e37b5SSimon Glass response = urllib2.urlopen(url) 408827e37b5SSimon Glass html = response.read() 409827e37b5SSimon Glass parser = MyHTMLParser(fetch_arch) 410827e37b5SSimon Glass parser.feed(html) 411827e37b5SSimon Glass if fetch_arch == 'list': 412827e37b5SSimon Glass links += parser.links 413827e37b5SSimon Glass elif parser.arch_link: 414827e37b5SSimon Glass return url + parser.arch_link 415827e37b5SSimon Glass if fetch_arch == 'list': 416827e37b5SSimon Glass return arch, links 417827e37b5SSimon Glass return None 418827e37b5SSimon Glass 419827e37b5SSimon Glass def Download(self, url): 420827e37b5SSimon Glass """Download a file to a temporary directory 421827e37b5SSimon Glass 422827e37b5SSimon Glass Args: 423827e37b5SSimon Glass url: URL to download 424827e37b5SSimon Glass Returns: 425827e37b5SSimon Glass Tuple: 426827e37b5SSimon Glass Temporary directory name 427827e37b5SSimon Glass Full path to the downloaded archive file in that directory, 428827e37b5SSimon Glass or None if there was an error while downloading 429827e37b5SSimon Glass """ 430ad24ebacSSimon Glass print 'Downloading: %s' % url 431827e37b5SSimon Glass leaf = url.split('/')[-1] 432827e37b5SSimon Glass tmpdir = tempfile.mkdtemp('.buildman') 433827e37b5SSimon Glass response = urllib2.urlopen(url) 434827e37b5SSimon Glass fname = os.path.join(tmpdir, leaf) 435827e37b5SSimon Glass fd = open(fname, 'wb') 436827e37b5SSimon Glass meta = response.info() 437ad24ebacSSimon Glass size = int(meta.getheaders('Content-Length')[0]) 438827e37b5SSimon Glass done = 0 439827e37b5SSimon Glass block_size = 1 << 16 440827e37b5SSimon Glass status = '' 441827e37b5SSimon Glass 442827e37b5SSimon Glass # Read the file in chunks and show progress as we go 443827e37b5SSimon Glass while True: 444827e37b5SSimon Glass buffer = response.read(block_size) 445827e37b5SSimon Glass if not buffer: 446827e37b5SSimon Glass print chr(8) * (len(status) + 1), '\r', 447827e37b5SSimon Glass break 448827e37b5SSimon Glass 449827e37b5SSimon Glass done += len(buffer) 450827e37b5SSimon Glass fd.write(buffer) 451ad24ebacSSimon Glass status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024, 452827e37b5SSimon Glass done * 100 / size) 453827e37b5SSimon Glass status = status + chr(8) * (len(status) + 1) 454827e37b5SSimon Glass print status, 455827e37b5SSimon Glass sys.stdout.flush() 456827e37b5SSimon Glass fd.close() 457827e37b5SSimon Glass if done != size: 458827e37b5SSimon Glass print 'Error, failed to download' 459827e37b5SSimon Glass os.remove(fname) 460827e37b5SSimon Glass fname = None 461827e37b5SSimon Glass return tmpdir, fname 462827e37b5SSimon Glass 463827e37b5SSimon Glass def Unpack(self, fname, dest): 464827e37b5SSimon Glass """Unpack a tar file 465827e37b5SSimon Glass 466827e37b5SSimon Glass Args: 467827e37b5SSimon Glass fname: Filename to unpack 468827e37b5SSimon Glass dest: Destination directory 469827e37b5SSimon Glass Returns: 470827e37b5SSimon Glass Directory name of the first entry in the archive, without the 471827e37b5SSimon Glass trailing / 472827e37b5SSimon Glass """ 473827e37b5SSimon Glass stdout = command.Output('tar', 'xvfJ', fname, '-C', dest) 474827e37b5SSimon Glass return stdout.splitlines()[0][:-1] 475827e37b5SSimon Glass 476827e37b5SSimon Glass def TestSettingsHasPath(self, path): 477827e37b5SSimon Glass """Check if builmand will find this toolchain 478827e37b5SSimon Glass 479827e37b5SSimon Glass Returns: 480827e37b5SSimon Glass True if the path is in settings, False if not 481827e37b5SSimon Glass """ 482827e37b5SSimon Glass paths = self.GetPathList() 483827e37b5SSimon Glass return path in paths 484827e37b5SSimon Glass 485827e37b5SSimon Glass def ListArchs(self): 486827e37b5SSimon Glass """List architectures with available toolchains to download""" 487827e37b5SSimon Glass host_arch, archives = self.LocateArchUrl('list') 488827e37b5SSimon Glass re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*') 489827e37b5SSimon Glass arch_set = set() 490827e37b5SSimon Glass for archive in archives: 491827e37b5SSimon Glass # Remove the host architecture from the start 492827e37b5SSimon Glass arch = re_arch.match(archive[len(host_arch):]) 493827e37b5SSimon Glass if arch: 494827e37b5SSimon Glass arch_set.add(arch.group(1)) 495827e37b5SSimon Glass return sorted(arch_set) 496827e37b5SSimon Glass 497827e37b5SSimon Glass def FetchAndInstall(self, arch): 498827e37b5SSimon Glass """Fetch and install a new toolchain 499827e37b5SSimon Glass 500827e37b5SSimon Glass arch: 501827e37b5SSimon Glass Architecture to fetch, or 'list' to list 502827e37b5SSimon Glass """ 503827e37b5SSimon Glass # Fist get the URL for this architecture 504827e37b5SSimon Glass url = self.LocateArchUrl(arch) 505827e37b5SSimon Glass if not url: 506827e37b5SSimon Glass print ("Cannot find toolchain for arch '%s' - use 'list' to list" % 507827e37b5SSimon Glass arch) 508827e37b5SSimon Glass return 2 509827e37b5SSimon Glass home = os.environ['HOME'] 510827e37b5SSimon Glass dest = os.path.join(home, '.buildman-toolchains') 511827e37b5SSimon Glass if not os.path.exists(dest): 512827e37b5SSimon Glass os.mkdir(dest) 513827e37b5SSimon Glass 514827e37b5SSimon Glass # Download the tar file for this toolchain and unpack it 515827e37b5SSimon Glass tmpdir, tarfile = self.Download(url) 516827e37b5SSimon Glass if not tarfile: 517827e37b5SSimon Glass return 1 518827e37b5SSimon Glass print 'Unpacking to: %s' % dest, 519827e37b5SSimon Glass sys.stdout.flush() 520827e37b5SSimon Glass path = self.Unpack(tarfile, dest) 521827e37b5SSimon Glass os.remove(tarfile) 522827e37b5SSimon Glass os.rmdir(tmpdir) 523827e37b5SSimon Glass print 524827e37b5SSimon Glass 525827e37b5SSimon Glass # Check that the toolchain works 526827e37b5SSimon Glass print 'Testing' 527827e37b5SSimon Glass dirpath = os.path.join(dest, path) 5282a76a649SSimon Glass compiler_fname_list = self.ScanPath(dirpath, True) 5292a76a649SSimon Glass if not compiler_fname_list: 530827e37b5SSimon Glass print 'Could not locate C compiler - fetch failed.' 531827e37b5SSimon Glass return 1 5322a76a649SSimon Glass if len(compiler_fname_list) != 1: 5332a76a649SSimon Glass print ('Internal error, ambiguous toolchains: %s' % 5342a76a649SSimon Glass (', '.join(compiler_fname))) 5352a76a649SSimon Glass return 1 5362a76a649SSimon Glass toolchain = Toolchain(compiler_fname_list[0], True, True) 537827e37b5SSimon Glass 538827e37b5SSimon Glass # Make sure that it will be found by buildman 539827e37b5SSimon Glass if not self.TestSettingsHasPath(dirpath): 540827e37b5SSimon Glass print ("Adding 'download' to config file '%s'" % 541827e37b5SSimon Glass bsettings.config_fname) 542827e37b5SSimon Glass tools_dir = os.path.dirname(dirpath) 543827e37b5SSimon Glass bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir) 544827e37b5SSimon Glass return 0 545