11a459660SWolfgang Denk# SPDX-License-Identifier: GPL-2.0+ 283d290c5STom Rini# Copyright (c) 2012 The Chromium OS Authors. 3fc3fe1c2SSimon Glass# 4fc3fe1c2SSimon Glass 54281ad8eSSimon Glassimport re 6fc3fe1c2SSimon Glassimport glob 7827e37b5SSimon Glassfrom HTMLParser import HTMLParser 8fc3fe1c2SSimon Glassimport os 9827e37b5SSimon Glassimport sys 10827e37b5SSimon Glassimport tempfile 11827e37b5SSimon Glassimport urllib2 12fc3fe1c2SSimon Glass 13fc3fe1c2SSimon Glassimport bsettings 14fc3fe1c2SSimon Glassimport command 15713bea38SSimon Glassimport terminal 16fc3fe1c2SSimon Glass 1717bce66cSSimon Glass(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH, 1817bce66cSSimon 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 = [] 354c58d273SDaniel Schwierzeck self.re_arch = re.compile('[-_]%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) 434c58d273SDaniel Schwierzeck if self.re_arch.search(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) 57*00beb248SSimon Glass override_toolchain: Toolchain to use for sandbox, overriding the normal 58*00beb248SSimon Glass one 59fc3fe1c2SSimon Glass """ 60608e399fSSimon Glass def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC, 61*00beb248SSimon Glass arch=None, override_toolchain=None): 62fc3fe1c2SSimon Glass """Create a new toolchain object. 63fc3fe1c2SSimon Glass 64fc3fe1c2SSimon Glass Args: 65fc3fe1c2SSimon Glass fname: Filename of the gcc component 66fc3fe1c2SSimon Glass test: True to run the toolchain to test it 67ad24ebacSSimon Glass verbose: True to print out the information 68ff690df9SSimon Glass priority: Priority to use for this toolchain, or PRIORITY_CALC to 69ff690df9SSimon Glass calculate it 70fc3fe1c2SSimon Glass """ 71fc3fe1c2SSimon Glass self.gcc = fname 72fc3fe1c2SSimon Glass self.path = os.path.dirname(fname) 73*00beb248SSimon Glass self.override_toolchain = override_toolchain 74b5324123SSimon Glass 75b5324123SSimon Glass # Find the CROSS_COMPILE prefix to use for U-Boot. For example, 76b5324123SSimon Glass # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'. 77b5324123SSimon Glass basename = os.path.basename(fname) 78b5324123SSimon Glass pos = basename.rfind('-') 79b5324123SSimon Glass self.cross = basename[:pos + 1] if pos != -1 else '' 80b5324123SSimon Glass 81b5324123SSimon Glass # The architecture is the first part of the name 82fc3fe1c2SSimon Glass pos = self.cross.find('-') 83608e399fSSimon Glass if arch: 84608e399fSSimon Glass self.arch = arch 85608e399fSSimon Glass else: 86fc3fe1c2SSimon Glass self.arch = self.cross[:pos] if pos != -1 else 'sandbox' 87*00beb248SSimon Glass if self.arch == 'sandbox' and override_toolchain: 88*00beb248SSimon Glass self.gcc = override_toolchain 89fc3fe1c2SSimon Glass 90bb1501f2SSimon Glass env = self.MakeEnvironment(False) 91fc3fe1c2SSimon Glass 92fc3fe1c2SSimon Glass # As a basic sanity check, run the C compiler with --version 93fc3fe1c2SSimon Glass cmd = [fname, '--version'] 94ff690df9SSimon Glass if priority == PRIORITY_CALC: 95ff690df9SSimon Glass self.priority = self.GetPriority(fname) 96ff690df9SSimon Glass else: 97ff690df9SSimon Glass self.priority = priority 98fc3fe1c2SSimon Glass if test: 998bb2bddcSStephen Warren result = command.RunPipe([cmd], capture=True, env=env, 1008bb2bddcSStephen Warren raise_on_error=False) 101fc3fe1c2SSimon Glass self.ok = result.return_code == 0 102fc3fe1c2SSimon Glass if verbose: 103fc3fe1c2SSimon Glass print 'Tool chain test: ', 104fc3fe1c2SSimon Glass if self.ok: 105608e399fSSimon Glass print "OK, arch='%s', priority %d" % (self.arch, 106608e399fSSimon Glass self.priority) 107fc3fe1c2SSimon Glass else: 108fc3fe1c2SSimon Glass print 'BAD' 109fc3fe1c2SSimon Glass print 'Command: ', cmd 110fc3fe1c2SSimon Glass print result.stdout 111fc3fe1c2SSimon Glass print result.stderr 112fc3fe1c2SSimon Glass else: 113fc3fe1c2SSimon Glass self.ok = True 114fc3fe1c2SSimon Glass 115fc3fe1c2SSimon Glass def GetPriority(self, fname): 116fc3fe1c2SSimon Glass """Return the priority of the toolchain. 117fc3fe1c2SSimon Glass 118fc3fe1c2SSimon Glass Toolchains are ranked according to their suitability by their 119fc3fe1c2SSimon Glass filename prefix. 120fc3fe1c2SSimon Glass 121fc3fe1c2SSimon Glass Args: 122fc3fe1c2SSimon Glass fname: Filename of toolchain 123fc3fe1c2SSimon Glass Returns: 124ff690df9SSimon Glass Priority of toolchain, PRIORITY_CALC=highest, 20=lowest. 125fc3fe1c2SSimon Glass """ 1268708267fSMasahiro Yamada priority_list = ['-elf', '-unknown-linux-gnu', '-linux', 127546a6f3aSTom Rini '-none-linux-gnueabi', '-none-linux-gnueabihf', '-uclinux', 128546a6f3aSTom Rini '-none-eabi', '-gentoo-linux-gnu', '-linux-gnueabi', 129546a6f3aSTom Rini '-linux-gnueabihf', '-le-linux', '-uclinux'] 130fc3fe1c2SSimon Glass for prio in range(len(priority_list)): 131fc3fe1c2SSimon Glass if priority_list[prio] in fname: 132ff690df9SSimon Glass return PRIORITY_CALC + prio 133ff690df9SSimon Glass return PRIORITY_CALC + prio 134fc3fe1c2SSimon Glass 135d5fe013cSYork Sun def GetWrapper(self, show_warning=True): 136d5fe013cSYork Sun """Get toolchain wrapper from the setting file. 137d5fe013cSYork Sun """ 138d5fe013cSYork Sun value = '' 139d5fe013cSYork Sun for name, value in bsettings.GetItems('toolchain-wrapper'): 140d5fe013cSYork Sun if not value: 141d5fe013cSYork Sun print "Warning: Wrapper not found" 142d5fe013cSYork Sun if value: 143d5fe013cSYork Sun value = value + ' ' 144d5fe013cSYork Sun 145d5fe013cSYork Sun return value 146d5fe013cSYork Sun 147bb1501f2SSimon Glass def MakeEnvironment(self, full_path): 148fc3fe1c2SSimon Glass """Returns an environment for using the toolchain. 149fc3fe1c2SSimon Glass 150bb1501f2SSimon Glass Thie takes the current environment and adds CROSS_COMPILE so that 151b0e994c2SDaniel Schwierzeck the tool chain will operate correctly. This also disables localized 152b0e994c2SDaniel Schwierzeck output and possibly unicode encoded output of all build tools by 153b0e994c2SDaniel Schwierzeck adding LC_ALL=C. 154bb1501f2SSimon Glass 155bb1501f2SSimon Glass Args: 156bb1501f2SSimon Glass full_path: Return the full path in CROSS_COMPILE and don't set 157bb1501f2SSimon Glass PATH 158*00beb248SSimon Glass Returns: 159*00beb248SSimon Glass Dict containing the environemnt to use. This is based on the current 160*00beb248SSimon Glass environment, with changes as needed to CROSS_COMPILE, PATH and 161*00beb248SSimon Glass LC_ALL. 162fc3fe1c2SSimon Glass """ 163fc3fe1c2SSimon Glass env = dict(os.environ) 164d5fe013cSYork Sun wrapper = self.GetWrapper() 165d5fe013cSYork Sun 166*00beb248SSimon Glass if self.override_toolchain: 167*00beb248SSimon Glass # We'll use MakeArgs() to provide this 168*00beb248SSimon Glass pass 169*00beb248SSimon Glass elif full_path: 170d5fe013cSYork Sun env['CROSS_COMPILE'] = wrapper + os.path.join(self.path, self.cross) 171bb1501f2SSimon Glass else: 172d5fe013cSYork Sun env['CROSS_COMPILE'] = wrapper + self.cross 173f210b587SSimon Glass env['PATH'] = self.path + ':' + env['PATH'] 174bb1501f2SSimon Glass 175b0e994c2SDaniel Schwierzeck env['LC_ALL'] = 'C' 176b0e994c2SDaniel Schwierzeck 177fc3fe1c2SSimon Glass return env 178fc3fe1c2SSimon Glass 179*00beb248SSimon Glass def MakeArgs(self): 180*00beb248SSimon Glass """Create the 'make' arguments for a toolchain 181*00beb248SSimon Glass 182*00beb248SSimon Glass This is only used when the toolchain is being overridden. Since the 183*00beb248SSimon Glass U-Boot Makefile sets CC and HOSTCC explicitly we cannot rely on the 184*00beb248SSimon Glass environment (and MakeEnvironment()) to override these values. This 185*00beb248SSimon Glass function returns the arguments to accomplish this. 186*00beb248SSimon Glass 187*00beb248SSimon Glass Returns: 188*00beb248SSimon Glass List of arguments to pass to 'make' 189*00beb248SSimon Glass """ 190*00beb248SSimon Glass if self.override_toolchain: 191*00beb248SSimon Glass return ['HOSTCC=%s' % self.override_toolchain, 192*00beb248SSimon Glass 'CC=%s' % self.override_toolchain] 193*00beb248SSimon Glass return [] 194*00beb248SSimon Glass 195fc3fe1c2SSimon Glass 196fc3fe1c2SSimon Glassclass Toolchains: 197fc3fe1c2SSimon Glass """Manage a list of toolchains for building U-Boot 198fc3fe1c2SSimon Glass 199fc3fe1c2SSimon Glass We select one toolchain for each architecture type 200fc3fe1c2SSimon Glass 201fc3fe1c2SSimon Glass Public members: 202fc3fe1c2SSimon Glass toolchains: Dict of Toolchain objects, keyed by architecture name 20317bce66cSSimon Glass prefixes: Dict of prefixes to check, keyed by architecture. This can 20417bce66cSSimon Glass be a full path and toolchain prefix, for example 20517bce66cSSimon Glass {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of 20617bce66cSSimon Glass something on the search path, for example 20717bce66cSSimon Glass {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported. 208fc3fe1c2SSimon Glass paths: List of paths to check for toolchains (may contain wildcards) 209fc3fe1c2SSimon Glass """ 210fc3fe1c2SSimon Glass 211*00beb248SSimon Glass def __init__(self, override_toolchain=None): 212fc3fe1c2SSimon Glass self.toolchains = {} 21317bce66cSSimon Glass self.prefixes = {} 214fc3fe1c2SSimon Glass self.paths = [] 215*00beb248SSimon Glass self.override_toolchain = override_toolchain 216d4144e45SSimon Glass self._make_flags = dict(bsettings.GetItems('make-flags')) 217d4144e45SSimon Glass 21880e6a487SSimon Glass def GetPathList(self, show_warning=True): 219827e37b5SSimon Glass """Get a list of available toolchain paths 220827e37b5SSimon Glass 22180e6a487SSimon Glass Args: 22280e6a487SSimon Glass show_warning: True to show a warning if there are no tool chains. 22380e6a487SSimon Glass 224827e37b5SSimon Glass Returns: 225827e37b5SSimon Glass List of strings, each a path to a toolchain mentioned in the 226827e37b5SSimon Glass [toolchain] section of the settings file. 227827e37b5SSimon Glass """ 2284281ad8eSSimon Glass toolchains = bsettings.GetItems('toolchain') 22980e6a487SSimon Glass if show_warning and not toolchains: 230713bea38SSimon Glass print ("Warning: No tool chains. Please run 'buildman " 231713bea38SSimon Glass "--fetch-arch all' to download all available toolchains, or " 232713bea38SSimon Glass "add a [toolchain] section to your buildman config file " 233713bea38SSimon Glass "%s. See README for details" % 2341826a18dSMasahiro Yamada bsettings.config_fname) 2354281ad8eSSimon Glass 236827e37b5SSimon Glass paths = [] 2374281ad8eSSimon Glass for name, value in toolchains: 238fc3fe1c2SSimon Glass if '*' in value: 239827e37b5SSimon Glass paths += glob.glob(value) 240fc3fe1c2SSimon Glass else: 241827e37b5SSimon Glass paths.append(value) 242827e37b5SSimon Glass return paths 243827e37b5SSimon Glass 24480e6a487SSimon Glass def GetSettings(self, show_warning=True): 24580e6a487SSimon Glass """Get toolchain settings from the settings file. 24680e6a487SSimon Glass 24780e6a487SSimon Glass Args: 24880e6a487SSimon Glass show_warning: True to show a warning if there are no tool chains. 24980e6a487SSimon Glass """ 25017bce66cSSimon Glass self.prefixes = bsettings.GetItems('toolchain-prefix') 25180e6a487SSimon Glass self.paths += self.GetPathList(show_warning) 252fc3fe1c2SSimon Glass 253608e399fSSimon Glass def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC, 254608e399fSSimon Glass arch=None): 255fc3fe1c2SSimon Glass """Add a toolchain to our list 256fc3fe1c2SSimon Glass 257fc3fe1c2SSimon Glass We select the given toolchain as our preferred one for its 258fc3fe1c2SSimon Glass architecture if it is a higher priority than the others. 259fc3fe1c2SSimon Glass 260fc3fe1c2SSimon Glass Args: 261fc3fe1c2SSimon Glass fname: Filename of toolchain's gcc driver 262fc3fe1c2SSimon Glass test: True to run the toolchain to test it 263ff690df9SSimon Glass priority: Priority to use for this toolchain 264608e399fSSimon Glass arch: Toolchain architecture, or None if not known 265fc3fe1c2SSimon Glass """ 266*00beb248SSimon Glass toolchain = Toolchain(fname, test, verbose, priority, arch, 267*00beb248SSimon Glass self.override_toolchain) 268fc3fe1c2SSimon Glass add_it = toolchain.ok 269fc3fe1c2SSimon Glass if toolchain.arch in self.toolchains: 270fc3fe1c2SSimon Glass add_it = (toolchain.priority < 271fc3fe1c2SSimon Glass self.toolchains[toolchain.arch].priority) 272fc3fe1c2SSimon Glass if add_it: 273fc3fe1c2SSimon Glass self.toolchains[toolchain.arch] = toolchain 274ff690df9SSimon Glass elif verbose: 275ff690df9SSimon Glass print ("Toolchain '%s' at priority %d will be ignored because " 276ff690df9SSimon Glass "another toolchain for arch '%s' has priority %d" % 277ff690df9SSimon Glass (toolchain.gcc, toolchain.priority, toolchain.arch, 278ff690df9SSimon Glass self.toolchains[toolchain.arch].priority)) 279fc3fe1c2SSimon Glass 280827e37b5SSimon Glass def ScanPath(self, path, verbose): 281827e37b5SSimon Glass """Scan a path for a valid toolchain 282827e37b5SSimon Glass 283827e37b5SSimon Glass Args: 284827e37b5SSimon Glass path: Path to scan 285827e37b5SSimon Glass verbose: True to print out progress information 286827e37b5SSimon Glass Returns: 287827e37b5SSimon Glass Filename of C compiler if found, else None 288827e37b5SSimon Glass """ 289d9088983SAlbert ARIBAUD fnames = [] 290827e37b5SSimon Glass for subdir in ['.', 'bin', 'usr/bin']: 291827e37b5SSimon Glass dirname = os.path.join(path, subdir) 292827e37b5SSimon Glass if verbose: print " - looking in '%s'" % dirname 293827e37b5SSimon Glass for fname in glob.glob(dirname + '/*gcc'): 294827e37b5SSimon Glass if verbose: print " - found '%s'" % fname 295d9088983SAlbert ARIBAUD fnames.append(fname) 296d9088983SAlbert ARIBAUD return fnames 297827e37b5SSimon Glass 29817bce66cSSimon Glass def ScanPathEnv(self, fname): 29917bce66cSSimon Glass """Scan the PATH environment variable for a given filename. 30017bce66cSSimon Glass 30117bce66cSSimon Glass Args: 30217bce66cSSimon Glass fname: Filename to scan for 30317bce66cSSimon Glass Returns: 30417bce66cSSimon Glass List of matching pathanames, or [] if none 30517bce66cSSimon Glass """ 30617bce66cSSimon Glass pathname_list = [] 30717bce66cSSimon Glass for path in os.environ["PATH"].split(os.pathsep): 30817bce66cSSimon Glass path = path.strip('"') 30917bce66cSSimon Glass pathname = os.path.join(path, fname) 31017bce66cSSimon Glass if os.path.exists(pathname): 31117bce66cSSimon Glass pathname_list.append(pathname) 31217bce66cSSimon Glass return pathname_list 313827e37b5SSimon Glass 314fc3fe1c2SSimon Glass def Scan(self, verbose): 315fc3fe1c2SSimon Glass """Scan for available toolchains and select the best for each arch. 316fc3fe1c2SSimon Glass 317fc3fe1c2SSimon Glass We look for all the toolchains we can file, figure out the 318fc3fe1c2SSimon Glass architecture for each, and whether it works. Then we select the 319fc3fe1c2SSimon Glass highest priority toolchain for each arch. 320fc3fe1c2SSimon Glass 321fc3fe1c2SSimon Glass Args: 322fc3fe1c2SSimon Glass verbose: True to print out progress information 323fc3fe1c2SSimon Glass """ 324fc3fe1c2SSimon Glass if verbose: print 'Scanning for tool chains' 32517bce66cSSimon Glass for name, value in self.prefixes: 32617bce66cSSimon Glass if verbose: print " - scanning prefix '%s'" % value 32717bce66cSSimon Glass if os.path.exists(value): 32817bce66cSSimon Glass self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name) 32917bce66cSSimon Glass continue 33017bce66cSSimon Glass fname = value + 'gcc' 33117bce66cSSimon Glass if os.path.exists(fname): 33217bce66cSSimon Glass self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name) 33317bce66cSSimon Glass continue 33417bce66cSSimon Glass fname_list = self.ScanPathEnv(fname) 33517bce66cSSimon Glass for f in fname_list: 33617bce66cSSimon Glass self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name) 33717bce66cSSimon Glass if not fname_list: 33817bce66cSSimon Glass raise ValueError, ("No tool chain found for prefix '%s'" % 33917bce66cSSimon Glass value) 340fc3fe1c2SSimon Glass for path in self.paths: 341fc3fe1c2SSimon Glass if verbose: print " - scanning path '%s'" % path 342d9088983SAlbert ARIBAUD fnames = self.ScanPath(path, verbose) 343d9088983SAlbert ARIBAUD for fname in fnames: 344fc3fe1c2SSimon Glass self.Add(fname, True, verbose) 345fc3fe1c2SSimon Glass 346fc3fe1c2SSimon Glass def List(self): 347fc3fe1c2SSimon Glass """List out the selected toolchains for each architecture""" 348713bea38SSimon Glass col = terminal.Color() 349713bea38SSimon Glass print col.Color(col.BLUE, 'List of available toolchains (%d):' % 350713bea38SSimon Glass len(self.toolchains)) 351fc3fe1c2SSimon Glass if len(self.toolchains): 352fc3fe1c2SSimon Glass for key, value in sorted(self.toolchains.iteritems()): 353fc3fe1c2SSimon Glass print '%-10s: %s' % (key, value.gcc) 354fc3fe1c2SSimon Glass else: 355fc3fe1c2SSimon Glass print 'None' 356fc3fe1c2SSimon Glass 357fc3fe1c2SSimon Glass def Select(self, arch): 358fc3fe1c2SSimon Glass """Returns the toolchain for a given architecture 359fc3fe1c2SSimon Glass 360fc3fe1c2SSimon Glass Args: 361fc3fe1c2SSimon Glass args: Name of architecture (e.g. 'arm', 'ppc_8xx') 362fc3fe1c2SSimon Glass 363fc3fe1c2SSimon Glass returns: 364fc3fe1c2SSimon Glass toolchain object, or None if none found 365fc3fe1c2SSimon Glass """ 3669b83bfdcSSimon Glass for tag, value in bsettings.GetItems('toolchain-alias'): 3679b83bfdcSSimon Glass if arch == tag: 3689b83bfdcSSimon Glass for alias in value.split(): 3699b83bfdcSSimon Glass if alias in self.toolchains: 3709b83bfdcSSimon Glass return self.toolchains[alias] 371fc3fe1c2SSimon Glass 372fc3fe1c2SSimon Glass if not arch in self.toolchains: 373fc3fe1c2SSimon Glass raise ValueError, ("No tool chain found for arch '%s'" % arch) 374fc3fe1c2SSimon Glass return self.toolchains[arch] 3754281ad8eSSimon Glass 3764281ad8eSSimon Glass def ResolveReferences(self, var_dict, args): 3774281ad8eSSimon Glass """Resolve variable references in a string 3784281ad8eSSimon Glass 3794281ad8eSSimon Glass This converts ${blah} within the string to the value of blah. 3804281ad8eSSimon Glass This function works recursively. 3814281ad8eSSimon Glass 3824281ad8eSSimon Glass Args: 3834281ad8eSSimon Glass var_dict: Dictionary containing variables and their values 3844281ad8eSSimon Glass args: String containing make arguments 3854281ad8eSSimon Glass Returns: 3864281ad8eSSimon Glass Resolved string 3874281ad8eSSimon Glass 3884281ad8eSSimon Glass >>> bsettings.Setup() 3894281ad8eSSimon Glass >>> tcs = Toolchains() 3904281ad8eSSimon Glass >>> tcs.Add('fred', False) 3914281ad8eSSimon Glass >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ 3924281ad8eSSimon Glass 'second' : '2nd'} 3934281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') 3944281ad8eSSimon Glass 'this=OBLIQUE_set' 3954281ad8eSSimon Glass >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') 3964281ad8eSSimon Glass 'this=OBLIQUE_setfi2ndrstnd' 3974281ad8eSSimon Glass """ 398f60c9d4fSSimon Glass re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') 3994281ad8eSSimon Glass 4004281ad8eSSimon Glass while True: 4014281ad8eSSimon Glass m = re_var.search(args) 4024281ad8eSSimon Glass if not m: 4034281ad8eSSimon Glass break 4044281ad8eSSimon Glass lookup = m.group(0)[2:-1] 4054281ad8eSSimon Glass value = var_dict.get(lookup, '') 4064281ad8eSSimon Glass args = args[:m.start(0)] + value + args[m.end(0):] 4074281ad8eSSimon Glass return args 4084281ad8eSSimon Glass 4094281ad8eSSimon Glass def GetMakeArguments(self, board): 4104281ad8eSSimon Glass """Returns 'make' arguments for a given board 4114281ad8eSSimon Glass 4124281ad8eSSimon Glass The flags are in a section called 'make-flags'. Flags are named 4134281ad8eSSimon Glass after the target they represent, for example snapper9260=TESTING=1 4144281ad8eSSimon Glass will pass TESTING=1 to make when building the snapper9260 board. 4154281ad8eSSimon Glass 4164281ad8eSSimon Glass References to other boards can be added in the string also. For 4174281ad8eSSimon Glass example: 4184281ad8eSSimon Glass 4194281ad8eSSimon Glass [make-flags] 4204281ad8eSSimon Glass at91-boards=ENABLE_AT91_TEST=1 4214281ad8eSSimon Glass snapper9260=${at91-boards} BUILD_TAG=442 4224281ad8eSSimon Glass snapper9g45=${at91-boards} BUILD_TAG=443 4234281ad8eSSimon Glass 4244281ad8eSSimon Glass This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 4254281ad8eSSimon Glass and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. 4264281ad8eSSimon Glass 4274281ad8eSSimon Glass A special 'target' variable is set to the board target. 4284281ad8eSSimon Glass 4294281ad8eSSimon Glass Args: 4304281ad8eSSimon Glass board: Board object for the board to check. 4314281ad8eSSimon Glass Returns: 4324281ad8eSSimon Glass 'make' flags for that board, or '' if none 4334281ad8eSSimon Glass """ 4344281ad8eSSimon Glass self._make_flags['target'] = board.target 4354281ad8eSSimon Glass arg_str = self.ResolveReferences(self._make_flags, 4364281ad8eSSimon Glass self._make_flags.get(board.target, '')) 4374281ad8eSSimon Glass args = arg_str.split(' ') 4384281ad8eSSimon Glass i = 0 4394281ad8eSSimon Glass while i < len(args): 4404281ad8eSSimon Glass if not args[i]: 4414281ad8eSSimon Glass del args[i] 4424281ad8eSSimon Glass else: 4434281ad8eSSimon Glass i += 1 4444281ad8eSSimon Glass return args 445827e37b5SSimon Glass 446827e37b5SSimon Glass def LocateArchUrl(self, fetch_arch): 447827e37b5SSimon Glass """Find a toolchain available online 448827e37b5SSimon Glass 449827e37b5SSimon Glass Look in standard places for available toolchains. At present the 450827e37b5SSimon Glass only standard place is at kernel.org. 451827e37b5SSimon Glass 452827e37b5SSimon Glass Args: 453827e37b5SSimon Glass arch: Architecture to look for, or 'list' for all 454827e37b5SSimon Glass Returns: 455827e37b5SSimon Glass If fetch_arch is 'list', a tuple: 456827e37b5SSimon Glass Machine architecture (e.g. x86_64) 457827e37b5SSimon Glass List of toolchains 458827e37b5SSimon Glass else 459827e37b5SSimon Glass URL containing this toolchain, if avaialble, else None 460827e37b5SSimon Glass """ 461827e37b5SSimon Glass arch = command.OutputOneLine('uname', '-m') 462827e37b5SSimon Glass base = 'https://www.kernel.org/pub/tools/crosstool/files/bin' 4634c58d273SDaniel Schwierzeck versions = ['7.3.0', '6.4.0', '4.9.4'] 464827e37b5SSimon Glass links = [] 465827e37b5SSimon Glass for version in versions: 466827e37b5SSimon Glass url = '%s/%s/%s/' % (base, arch, version) 467827e37b5SSimon Glass print 'Checking: %s' % url 468827e37b5SSimon Glass response = urllib2.urlopen(url) 469827e37b5SSimon Glass html = response.read() 470827e37b5SSimon Glass parser = MyHTMLParser(fetch_arch) 471827e37b5SSimon Glass parser.feed(html) 472827e37b5SSimon Glass if fetch_arch == 'list': 473827e37b5SSimon Glass links += parser.links 474827e37b5SSimon Glass elif parser.arch_link: 475827e37b5SSimon Glass return url + parser.arch_link 476827e37b5SSimon Glass if fetch_arch == 'list': 477827e37b5SSimon Glass return arch, links 478827e37b5SSimon Glass return None 479827e37b5SSimon Glass 480827e37b5SSimon Glass def Download(self, url): 481827e37b5SSimon Glass """Download a file to a temporary directory 482827e37b5SSimon Glass 483827e37b5SSimon Glass Args: 484827e37b5SSimon Glass url: URL to download 485827e37b5SSimon Glass Returns: 486827e37b5SSimon Glass Tuple: 487827e37b5SSimon Glass Temporary directory name 488827e37b5SSimon Glass Full path to the downloaded archive file in that directory, 489827e37b5SSimon Glass or None if there was an error while downloading 490827e37b5SSimon Glass """ 491ad24ebacSSimon Glass print 'Downloading: %s' % url 492827e37b5SSimon Glass leaf = url.split('/')[-1] 493827e37b5SSimon Glass tmpdir = tempfile.mkdtemp('.buildman') 494827e37b5SSimon Glass response = urllib2.urlopen(url) 495827e37b5SSimon Glass fname = os.path.join(tmpdir, leaf) 496827e37b5SSimon Glass fd = open(fname, 'wb') 497827e37b5SSimon Glass meta = response.info() 498ad24ebacSSimon Glass size = int(meta.getheaders('Content-Length')[0]) 499827e37b5SSimon Glass done = 0 500827e37b5SSimon Glass block_size = 1 << 16 501827e37b5SSimon Glass status = '' 502827e37b5SSimon Glass 503827e37b5SSimon Glass # Read the file in chunks and show progress as we go 504827e37b5SSimon Glass while True: 505827e37b5SSimon Glass buffer = response.read(block_size) 506827e37b5SSimon Glass if not buffer: 507827e37b5SSimon Glass print chr(8) * (len(status) + 1), '\r', 508827e37b5SSimon Glass break 509827e37b5SSimon Glass 510827e37b5SSimon Glass done += len(buffer) 511827e37b5SSimon Glass fd.write(buffer) 512ad24ebacSSimon Glass status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024, 513827e37b5SSimon Glass done * 100 / size) 514827e37b5SSimon Glass status = status + chr(8) * (len(status) + 1) 515827e37b5SSimon Glass print status, 516827e37b5SSimon Glass sys.stdout.flush() 517827e37b5SSimon Glass fd.close() 518827e37b5SSimon Glass if done != size: 519827e37b5SSimon Glass print 'Error, failed to download' 520827e37b5SSimon Glass os.remove(fname) 521827e37b5SSimon Glass fname = None 522827e37b5SSimon Glass return tmpdir, fname 523827e37b5SSimon Glass 524827e37b5SSimon Glass def Unpack(self, fname, dest): 525827e37b5SSimon Glass """Unpack a tar file 526827e37b5SSimon Glass 527827e37b5SSimon Glass Args: 528827e37b5SSimon Glass fname: Filename to unpack 529827e37b5SSimon Glass dest: Destination directory 530827e37b5SSimon Glass Returns: 531827e37b5SSimon Glass Directory name of the first entry in the archive, without the 532827e37b5SSimon Glass trailing / 533827e37b5SSimon Glass """ 534827e37b5SSimon Glass stdout = command.Output('tar', 'xvfJ', fname, '-C', dest) 535d82f539aSTrevor Woerner dirs = stdout.splitlines()[1].split('/')[:2] 536d82f539aSTrevor Woerner return '/'.join(dirs) 537827e37b5SSimon Glass 538827e37b5SSimon Glass def TestSettingsHasPath(self, path): 5392289b276SSimon Glass """Check if buildman will find this toolchain 540827e37b5SSimon Glass 541827e37b5SSimon Glass Returns: 542827e37b5SSimon Glass True if the path is in settings, False if not 543827e37b5SSimon Glass """ 54480e6a487SSimon Glass paths = self.GetPathList(False) 545827e37b5SSimon Glass return path in paths 546827e37b5SSimon Glass 547827e37b5SSimon Glass def ListArchs(self): 548827e37b5SSimon Glass """List architectures with available toolchains to download""" 549827e37b5SSimon Glass host_arch, archives = self.LocateArchUrl('list') 550b11f1264STrevor Woerner re_arch = re.compile('[-a-z0-9.]*[-_]([^-]*)-.*') 551827e37b5SSimon Glass arch_set = set() 552827e37b5SSimon Glass for archive in archives: 553827e37b5SSimon Glass # Remove the host architecture from the start 554827e37b5SSimon Glass arch = re_arch.match(archive[len(host_arch):]) 555827e37b5SSimon Glass if arch: 556b11f1264STrevor Woerner if arch.group(1) != '2.0' and arch.group(1) != '64': 557827e37b5SSimon Glass arch_set.add(arch.group(1)) 558827e37b5SSimon Glass return sorted(arch_set) 559827e37b5SSimon Glass 560827e37b5SSimon Glass def FetchAndInstall(self, arch): 561827e37b5SSimon Glass """Fetch and install a new toolchain 562827e37b5SSimon Glass 563827e37b5SSimon Glass arch: 564827e37b5SSimon Glass Architecture to fetch, or 'list' to list 565827e37b5SSimon Glass """ 566827e37b5SSimon Glass # Fist get the URL for this architecture 567713bea38SSimon Glass col = terminal.Color() 568713bea38SSimon Glass print col.Color(col.BLUE, "Downloading toolchain for arch '%s'" % arch) 569827e37b5SSimon Glass url = self.LocateArchUrl(arch) 570827e37b5SSimon Glass if not url: 571827e37b5SSimon Glass print ("Cannot find toolchain for arch '%s' - use 'list' to list" % 572827e37b5SSimon Glass arch) 573827e37b5SSimon Glass return 2 574827e37b5SSimon Glass home = os.environ['HOME'] 575827e37b5SSimon Glass dest = os.path.join(home, '.buildman-toolchains') 576827e37b5SSimon Glass if not os.path.exists(dest): 577827e37b5SSimon Glass os.mkdir(dest) 578827e37b5SSimon Glass 579827e37b5SSimon Glass # Download the tar file for this toolchain and unpack it 580827e37b5SSimon Glass tmpdir, tarfile = self.Download(url) 581827e37b5SSimon Glass if not tarfile: 582827e37b5SSimon Glass return 1 583713bea38SSimon Glass print col.Color(col.GREEN, 'Unpacking to: %s' % dest), 584827e37b5SSimon Glass sys.stdout.flush() 585827e37b5SSimon Glass path = self.Unpack(tarfile, dest) 586827e37b5SSimon Glass os.remove(tarfile) 587827e37b5SSimon Glass os.rmdir(tmpdir) 588827e37b5SSimon Glass print 589827e37b5SSimon Glass 590827e37b5SSimon Glass # Check that the toolchain works 591713bea38SSimon Glass print col.Color(col.GREEN, 'Testing') 592827e37b5SSimon Glass dirpath = os.path.join(dest, path) 5932a76a649SSimon Glass compiler_fname_list = self.ScanPath(dirpath, True) 5942a76a649SSimon Glass if not compiler_fname_list: 595827e37b5SSimon Glass print 'Could not locate C compiler - fetch failed.' 596827e37b5SSimon Glass return 1 5972a76a649SSimon Glass if len(compiler_fname_list) != 1: 598713bea38SSimon Glass print col.Color(col.RED, 'Warning, ambiguous toolchains: %s' % 599713bea38SSimon Glass ', '.join(compiler_fname_list)) 6002a76a649SSimon Glass toolchain = Toolchain(compiler_fname_list[0], True, True) 601827e37b5SSimon Glass 602827e37b5SSimon Glass # Make sure that it will be found by buildman 603827e37b5SSimon Glass if not self.TestSettingsHasPath(dirpath): 604827e37b5SSimon Glass print ("Adding 'download' to config file '%s'" % 605827e37b5SSimon Glass bsettings.config_fname) 606c8785c5bSSimon Glass bsettings.SetItem('toolchain', 'download', '%s/*/*' % dest) 607827e37b5SSimon Glass return 0 608