1*4882a593Smuzhiyun# Copyright (c) 2012 The Chromium OS Authors. 2*4882a593Smuzhiyun# 3*4882a593Smuzhiyun# SPDX-License-Identifier: GPL-2.0+ 4*4882a593Smuzhiyun# 5*4882a593Smuzhiyun 6*4882a593Smuzhiyunimport re 7*4882a593Smuzhiyunimport glob 8*4882a593Smuzhiyunfrom HTMLParser import HTMLParser 9*4882a593Smuzhiyunimport os 10*4882a593Smuzhiyunimport sys 11*4882a593Smuzhiyunimport tempfile 12*4882a593Smuzhiyunimport urllib2 13*4882a593Smuzhiyun 14*4882a593Smuzhiyunimport bsettings 15*4882a593Smuzhiyunimport command 16*4882a593Smuzhiyunimport terminal 17*4882a593Smuzhiyun 18*4882a593Smuzhiyun(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH, 19*4882a593Smuzhiyun PRIORITY_CALC) = range(4) 20*4882a593Smuzhiyun 21*4882a593Smuzhiyun# Simple class to collect links from a page 22*4882a593Smuzhiyunclass MyHTMLParser(HTMLParser): 23*4882a593Smuzhiyun def __init__(self, arch): 24*4882a593Smuzhiyun """Create a new parser 25*4882a593Smuzhiyun 26*4882a593Smuzhiyun After the parser runs, self.links will be set to a list of the links 27*4882a593Smuzhiyun to .xz archives found in the page, and self.arch_link will be set to 28*4882a593Smuzhiyun the one for the given architecture (or None if not found). 29*4882a593Smuzhiyun 30*4882a593Smuzhiyun Args: 31*4882a593Smuzhiyun arch: Architecture to search for 32*4882a593Smuzhiyun """ 33*4882a593Smuzhiyun HTMLParser.__init__(self) 34*4882a593Smuzhiyun self.arch_link = None 35*4882a593Smuzhiyun self.links = [] 36*4882a593Smuzhiyun self._match = '_%s-' % arch 37*4882a593Smuzhiyun 38*4882a593Smuzhiyun def handle_starttag(self, tag, attrs): 39*4882a593Smuzhiyun if tag == 'a': 40*4882a593Smuzhiyun for tag, value in attrs: 41*4882a593Smuzhiyun if tag == 'href': 42*4882a593Smuzhiyun if value and value.endswith('.xz'): 43*4882a593Smuzhiyun self.links.append(value) 44*4882a593Smuzhiyun if self._match in value: 45*4882a593Smuzhiyun self.arch_link = value 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun 48*4882a593Smuzhiyunclass Toolchain: 49*4882a593Smuzhiyun """A single toolchain 50*4882a593Smuzhiyun 51*4882a593Smuzhiyun Public members: 52*4882a593Smuzhiyun gcc: Full path to C compiler 53*4882a593Smuzhiyun path: Directory path containing C compiler 54*4882a593Smuzhiyun cross: Cross compile string, e.g. 'arm-linux-' 55*4882a593Smuzhiyun arch: Architecture of toolchain as determined from the first 56*4882a593Smuzhiyun component of the filename. E.g. arm-linux-gcc becomes arm 57*4882a593Smuzhiyun priority: Toolchain priority (0=highest, 20=lowest) 58*4882a593Smuzhiyun """ 59*4882a593Smuzhiyun def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC, 60*4882a593Smuzhiyun arch=None): 61*4882a593Smuzhiyun """Create a new toolchain object. 62*4882a593Smuzhiyun 63*4882a593Smuzhiyun Args: 64*4882a593Smuzhiyun fname: Filename of the gcc component 65*4882a593Smuzhiyun test: True to run the toolchain to test it 66*4882a593Smuzhiyun verbose: True to print out the information 67*4882a593Smuzhiyun priority: Priority to use for this toolchain, or PRIORITY_CALC to 68*4882a593Smuzhiyun calculate it 69*4882a593Smuzhiyun """ 70*4882a593Smuzhiyun self.gcc = fname 71*4882a593Smuzhiyun self.path = os.path.dirname(fname) 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun # Find the CROSS_COMPILE prefix to use for U-Boot. For example, 74*4882a593Smuzhiyun # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'. 75*4882a593Smuzhiyun basename = os.path.basename(fname) 76*4882a593Smuzhiyun pos = basename.rfind('-') 77*4882a593Smuzhiyun self.cross = basename[:pos + 1] if pos != -1 else '' 78*4882a593Smuzhiyun 79*4882a593Smuzhiyun # The architecture is the first part of the name 80*4882a593Smuzhiyun pos = self.cross.find('-') 81*4882a593Smuzhiyun if arch: 82*4882a593Smuzhiyun self.arch = arch 83*4882a593Smuzhiyun else: 84*4882a593Smuzhiyun self.arch = self.cross[:pos] if pos != -1 else 'sandbox' 85*4882a593Smuzhiyun 86*4882a593Smuzhiyun env = self.MakeEnvironment(False) 87*4882a593Smuzhiyun 88*4882a593Smuzhiyun # As a basic sanity check, run the C compiler with --version 89*4882a593Smuzhiyun cmd = [fname, '--version'] 90*4882a593Smuzhiyun if priority == PRIORITY_CALC: 91*4882a593Smuzhiyun self.priority = self.GetPriority(fname) 92*4882a593Smuzhiyun else: 93*4882a593Smuzhiyun self.priority = priority 94*4882a593Smuzhiyun if test: 95*4882a593Smuzhiyun result = command.RunPipe([cmd], capture=True, env=env, 96*4882a593Smuzhiyun raise_on_error=False) 97*4882a593Smuzhiyun self.ok = result.return_code == 0 98*4882a593Smuzhiyun if verbose: 99*4882a593Smuzhiyun print 'Tool chain test: ', 100*4882a593Smuzhiyun if self.ok: 101*4882a593Smuzhiyun print "OK, arch='%s', priority %d" % (self.arch, 102*4882a593Smuzhiyun self.priority) 103*4882a593Smuzhiyun else: 104*4882a593Smuzhiyun print 'BAD' 105*4882a593Smuzhiyun print 'Command: ', cmd 106*4882a593Smuzhiyun print result.stdout 107*4882a593Smuzhiyun print result.stderr 108*4882a593Smuzhiyun else: 109*4882a593Smuzhiyun self.ok = True 110*4882a593Smuzhiyun 111*4882a593Smuzhiyun def GetPriority(self, fname): 112*4882a593Smuzhiyun """Return the priority of the toolchain. 113*4882a593Smuzhiyun 114*4882a593Smuzhiyun Toolchains are ranked according to their suitability by their 115*4882a593Smuzhiyun filename prefix. 116*4882a593Smuzhiyun 117*4882a593Smuzhiyun Args: 118*4882a593Smuzhiyun fname: Filename of toolchain 119*4882a593Smuzhiyun Returns: 120*4882a593Smuzhiyun Priority of toolchain, PRIORITY_CALC=highest, 20=lowest. 121*4882a593Smuzhiyun """ 122*4882a593Smuzhiyun priority_list = ['-elf', '-unknown-linux-gnu', '-linux', 123*4882a593Smuzhiyun '-none-linux-gnueabi', '-none-linux-gnueabihf', '-uclinux', 124*4882a593Smuzhiyun '-none-eabi', '-gentoo-linux-gnu', '-linux-gnueabi', 125*4882a593Smuzhiyun '-linux-gnueabihf', '-le-linux', '-uclinux'] 126*4882a593Smuzhiyun for prio in range(len(priority_list)): 127*4882a593Smuzhiyun if priority_list[prio] in fname: 128*4882a593Smuzhiyun return PRIORITY_CALC + prio 129*4882a593Smuzhiyun return PRIORITY_CALC + prio 130*4882a593Smuzhiyun 131*4882a593Smuzhiyun def GetWrapper(self, show_warning=True): 132*4882a593Smuzhiyun """Get toolchain wrapper from the setting file. 133*4882a593Smuzhiyun """ 134*4882a593Smuzhiyun value = '' 135*4882a593Smuzhiyun for name, value in bsettings.GetItems('toolchain-wrapper'): 136*4882a593Smuzhiyun if not value: 137*4882a593Smuzhiyun print "Warning: Wrapper not found" 138*4882a593Smuzhiyun if value: 139*4882a593Smuzhiyun value = value + ' ' 140*4882a593Smuzhiyun 141*4882a593Smuzhiyun return value 142*4882a593Smuzhiyun 143*4882a593Smuzhiyun def MakeEnvironment(self, full_path): 144*4882a593Smuzhiyun """Returns an environment for using the toolchain. 145*4882a593Smuzhiyun 146*4882a593Smuzhiyun Thie takes the current environment and adds CROSS_COMPILE so that 147*4882a593Smuzhiyun the tool chain will operate correctly. This also disables localized 148*4882a593Smuzhiyun output and possibly unicode encoded output of all build tools by 149*4882a593Smuzhiyun adding LC_ALL=C. 150*4882a593Smuzhiyun 151*4882a593Smuzhiyun Args: 152*4882a593Smuzhiyun full_path: Return the full path in CROSS_COMPILE and don't set 153*4882a593Smuzhiyun PATH 154*4882a593Smuzhiyun """ 155*4882a593Smuzhiyun env = dict(os.environ) 156*4882a593Smuzhiyun wrapper = self.GetWrapper() 157*4882a593Smuzhiyun 158*4882a593Smuzhiyun if full_path: 159*4882a593Smuzhiyun env['CROSS_COMPILE'] = wrapper + os.path.join(self.path, self.cross) 160*4882a593Smuzhiyun else: 161*4882a593Smuzhiyun env['CROSS_COMPILE'] = wrapper + self.cross 162*4882a593Smuzhiyun env['PATH'] = self.path + ':' + env['PATH'] 163*4882a593Smuzhiyun 164*4882a593Smuzhiyun env['LC_ALL'] = 'C' 165*4882a593Smuzhiyun 166*4882a593Smuzhiyun return env 167*4882a593Smuzhiyun 168*4882a593Smuzhiyun 169*4882a593Smuzhiyunclass Toolchains: 170*4882a593Smuzhiyun """Manage a list of toolchains for building U-Boot 171*4882a593Smuzhiyun 172*4882a593Smuzhiyun We select one toolchain for each architecture type 173*4882a593Smuzhiyun 174*4882a593Smuzhiyun Public members: 175*4882a593Smuzhiyun toolchains: Dict of Toolchain objects, keyed by architecture name 176*4882a593Smuzhiyun prefixes: Dict of prefixes to check, keyed by architecture. This can 177*4882a593Smuzhiyun be a full path and toolchain prefix, for example 178*4882a593Smuzhiyun {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of 179*4882a593Smuzhiyun something on the search path, for example 180*4882a593Smuzhiyun {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported. 181*4882a593Smuzhiyun paths: List of paths to check for toolchains (may contain wildcards) 182*4882a593Smuzhiyun """ 183*4882a593Smuzhiyun 184*4882a593Smuzhiyun def __init__(self): 185*4882a593Smuzhiyun self.toolchains = {} 186*4882a593Smuzhiyun self.prefixes = {} 187*4882a593Smuzhiyun self.paths = [] 188*4882a593Smuzhiyun self._make_flags = dict(bsettings.GetItems('make-flags')) 189*4882a593Smuzhiyun 190*4882a593Smuzhiyun def GetPathList(self, show_warning=True): 191*4882a593Smuzhiyun """Get a list of available toolchain paths 192*4882a593Smuzhiyun 193*4882a593Smuzhiyun Args: 194*4882a593Smuzhiyun show_warning: True to show a warning if there are no tool chains. 195*4882a593Smuzhiyun 196*4882a593Smuzhiyun Returns: 197*4882a593Smuzhiyun List of strings, each a path to a toolchain mentioned in the 198*4882a593Smuzhiyun [toolchain] section of the settings file. 199*4882a593Smuzhiyun """ 200*4882a593Smuzhiyun toolchains = bsettings.GetItems('toolchain') 201*4882a593Smuzhiyun if show_warning and not toolchains: 202*4882a593Smuzhiyun print ("Warning: No tool chains. Please run 'buildman " 203*4882a593Smuzhiyun "--fetch-arch all' to download all available toolchains, or " 204*4882a593Smuzhiyun "add a [toolchain] section to your buildman config file " 205*4882a593Smuzhiyun "%s. See README for details" % 206*4882a593Smuzhiyun bsettings.config_fname) 207*4882a593Smuzhiyun 208*4882a593Smuzhiyun paths = [] 209*4882a593Smuzhiyun for name, value in toolchains: 210*4882a593Smuzhiyun if '*' in value: 211*4882a593Smuzhiyun paths += glob.glob(value) 212*4882a593Smuzhiyun else: 213*4882a593Smuzhiyun paths.append(value) 214*4882a593Smuzhiyun return paths 215*4882a593Smuzhiyun 216*4882a593Smuzhiyun def GetSettings(self, show_warning=True): 217*4882a593Smuzhiyun """Get toolchain settings from the settings file. 218*4882a593Smuzhiyun 219*4882a593Smuzhiyun Args: 220*4882a593Smuzhiyun show_warning: True to show a warning if there are no tool chains. 221*4882a593Smuzhiyun """ 222*4882a593Smuzhiyun self.prefixes = bsettings.GetItems('toolchain-prefix') 223*4882a593Smuzhiyun self.paths += self.GetPathList(show_warning) 224*4882a593Smuzhiyun 225*4882a593Smuzhiyun def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC, 226*4882a593Smuzhiyun arch=None): 227*4882a593Smuzhiyun """Add a toolchain to our list 228*4882a593Smuzhiyun 229*4882a593Smuzhiyun We select the given toolchain as our preferred one for its 230*4882a593Smuzhiyun architecture if it is a higher priority than the others. 231*4882a593Smuzhiyun 232*4882a593Smuzhiyun Args: 233*4882a593Smuzhiyun fname: Filename of toolchain's gcc driver 234*4882a593Smuzhiyun test: True to run the toolchain to test it 235*4882a593Smuzhiyun priority: Priority to use for this toolchain 236*4882a593Smuzhiyun arch: Toolchain architecture, or None if not known 237*4882a593Smuzhiyun """ 238*4882a593Smuzhiyun toolchain = Toolchain(fname, test, verbose, priority, arch) 239*4882a593Smuzhiyun add_it = toolchain.ok 240*4882a593Smuzhiyun if toolchain.arch in self.toolchains: 241*4882a593Smuzhiyun add_it = (toolchain.priority < 242*4882a593Smuzhiyun self.toolchains[toolchain.arch].priority) 243*4882a593Smuzhiyun if add_it: 244*4882a593Smuzhiyun self.toolchains[toolchain.arch] = toolchain 245*4882a593Smuzhiyun elif verbose: 246*4882a593Smuzhiyun print ("Toolchain '%s' at priority %d will be ignored because " 247*4882a593Smuzhiyun "another toolchain for arch '%s' has priority %d" % 248*4882a593Smuzhiyun (toolchain.gcc, toolchain.priority, toolchain.arch, 249*4882a593Smuzhiyun self.toolchains[toolchain.arch].priority)) 250*4882a593Smuzhiyun 251*4882a593Smuzhiyun def ScanPath(self, path, verbose): 252*4882a593Smuzhiyun """Scan a path for a valid toolchain 253*4882a593Smuzhiyun 254*4882a593Smuzhiyun Args: 255*4882a593Smuzhiyun path: Path to scan 256*4882a593Smuzhiyun verbose: True to print out progress information 257*4882a593Smuzhiyun Returns: 258*4882a593Smuzhiyun Filename of C compiler if found, else None 259*4882a593Smuzhiyun """ 260*4882a593Smuzhiyun fnames = [] 261*4882a593Smuzhiyun for subdir in ['.', 'bin', 'usr/bin']: 262*4882a593Smuzhiyun dirname = os.path.join(path, subdir) 263*4882a593Smuzhiyun if verbose: print " - looking in '%s'" % dirname 264*4882a593Smuzhiyun for fname in glob.glob(dirname + '/*gcc'): 265*4882a593Smuzhiyun if verbose: print " - found '%s'" % fname 266*4882a593Smuzhiyun fnames.append(fname) 267*4882a593Smuzhiyun return fnames 268*4882a593Smuzhiyun 269*4882a593Smuzhiyun def ScanPathEnv(self, fname): 270*4882a593Smuzhiyun """Scan the PATH environment variable for a given filename. 271*4882a593Smuzhiyun 272*4882a593Smuzhiyun Args: 273*4882a593Smuzhiyun fname: Filename to scan for 274*4882a593Smuzhiyun Returns: 275*4882a593Smuzhiyun List of matching pathanames, or [] if none 276*4882a593Smuzhiyun """ 277*4882a593Smuzhiyun pathname_list = [] 278*4882a593Smuzhiyun for path in os.environ["PATH"].split(os.pathsep): 279*4882a593Smuzhiyun path = path.strip('"') 280*4882a593Smuzhiyun pathname = os.path.join(path, fname) 281*4882a593Smuzhiyun if os.path.exists(pathname): 282*4882a593Smuzhiyun pathname_list.append(pathname) 283*4882a593Smuzhiyun return pathname_list 284*4882a593Smuzhiyun 285*4882a593Smuzhiyun def Scan(self, verbose): 286*4882a593Smuzhiyun """Scan for available toolchains and select the best for each arch. 287*4882a593Smuzhiyun 288*4882a593Smuzhiyun We look for all the toolchains we can file, figure out the 289*4882a593Smuzhiyun architecture for each, and whether it works. Then we select the 290*4882a593Smuzhiyun highest priority toolchain for each arch. 291*4882a593Smuzhiyun 292*4882a593Smuzhiyun Args: 293*4882a593Smuzhiyun verbose: True to print out progress information 294*4882a593Smuzhiyun """ 295*4882a593Smuzhiyun if verbose: print 'Scanning for tool chains' 296*4882a593Smuzhiyun for name, value in self.prefixes: 297*4882a593Smuzhiyun if verbose: print " - scanning prefix '%s'" % value 298*4882a593Smuzhiyun if os.path.exists(value): 299*4882a593Smuzhiyun self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name) 300*4882a593Smuzhiyun continue 301*4882a593Smuzhiyun fname = value + 'gcc' 302*4882a593Smuzhiyun if os.path.exists(fname): 303*4882a593Smuzhiyun self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name) 304*4882a593Smuzhiyun continue 305*4882a593Smuzhiyun fname_list = self.ScanPathEnv(fname) 306*4882a593Smuzhiyun for f in fname_list: 307*4882a593Smuzhiyun self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name) 308*4882a593Smuzhiyun if not fname_list: 309*4882a593Smuzhiyun raise ValueError, ("No tool chain found for prefix '%s'" % 310*4882a593Smuzhiyun value) 311*4882a593Smuzhiyun for path in self.paths: 312*4882a593Smuzhiyun if verbose: print " - scanning path '%s'" % path 313*4882a593Smuzhiyun fnames = self.ScanPath(path, verbose) 314*4882a593Smuzhiyun for fname in fnames: 315*4882a593Smuzhiyun self.Add(fname, True, verbose) 316*4882a593Smuzhiyun 317*4882a593Smuzhiyun def List(self): 318*4882a593Smuzhiyun """List out the selected toolchains for each architecture""" 319*4882a593Smuzhiyun col = terminal.Color() 320*4882a593Smuzhiyun print col.Color(col.BLUE, 'List of available toolchains (%d):' % 321*4882a593Smuzhiyun len(self.toolchains)) 322*4882a593Smuzhiyun if len(self.toolchains): 323*4882a593Smuzhiyun for key, value in sorted(self.toolchains.iteritems()): 324*4882a593Smuzhiyun print '%-10s: %s' % (key, value.gcc) 325*4882a593Smuzhiyun else: 326*4882a593Smuzhiyun print 'None' 327*4882a593Smuzhiyun 328*4882a593Smuzhiyun def Select(self, arch): 329*4882a593Smuzhiyun """Returns the toolchain for a given architecture 330*4882a593Smuzhiyun 331*4882a593Smuzhiyun Args: 332*4882a593Smuzhiyun args: Name of architecture (e.g. 'arm', 'ppc_8xx') 333*4882a593Smuzhiyun 334*4882a593Smuzhiyun returns: 335*4882a593Smuzhiyun toolchain object, or None if none found 336*4882a593Smuzhiyun """ 337*4882a593Smuzhiyun for tag, value in bsettings.GetItems('toolchain-alias'): 338*4882a593Smuzhiyun if arch == tag: 339*4882a593Smuzhiyun for alias in value.split(): 340*4882a593Smuzhiyun if alias in self.toolchains: 341*4882a593Smuzhiyun return self.toolchains[alias] 342*4882a593Smuzhiyun 343*4882a593Smuzhiyun if not arch in self.toolchains: 344*4882a593Smuzhiyun raise ValueError, ("No tool chain found for arch '%s'" % arch) 345*4882a593Smuzhiyun return self.toolchains[arch] 346*4882a593Smuzhiyun 347*4882a593Smuzhiyun def ResolveReferences(self, var_dict, args): 348*4882a593Smuzhiyun """Resolve variable references in a string 349*4882a593Smuzhiyun 350*4882a593Smuzhiyun This converts ${blah} within the string to the value of blah. 351*4882a593Smuzhiyun This function works recursively. 352*4882a593Smuzhiyun 353*4882a593Smuzhiyun Args: 354*4882a593Smuzhiyun var_dict: Dictionary containing variables and their values 355*4882a593Smuzhiyun args: String containing make arguments 356*4882a593Smuzhiyun Returns: 357*4882a593Smuzhiyun Resolved string 358*4882a593Smuzhiyun 359*4882a593Smuzhiyun >>> bsettings.Setup() 360*4882a593Smuzhiyun >>> tcs = Toolchains() 361*4882a593Smuzhiyun >>> tcs.Add('fred', False) 362*4882a593Smuzhiyun >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ 363*4882a593Smuzhiyun 'second' : '2nd'} 364*4882a593Smuzhiyun >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') 365*4882a593Smuzhiyun 'this=OBLIQUE_set' 366*4882a593Smuzhiyun >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') 367*4882a593Smuzhiyun 'this=OBLIQUE_setfi2ndrstnd' 368*4882a593Smuzhiyun """ 369*4882a593Smuzhiyun re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') 370*4882a593Smuzhiyun 371*4882a593Smuzhiyun while True: 372*4882a593Smuzhiyun m = re_var.search(args) 373*4882a593Smuzhiyun if not m: 374*4882a593Smuzhiyun break 375*4882a593Smuzhiyun lookup = m.group(0)[2:-1] 376*4882a593Smuzhiyun value = var_dict.get(lookup, '') 377*4882a593Smuzhiyun args = args[:m.start(0)] + value + args[m.end(0):] 378*4882a593Smuzhiyun return args 379*4882a593Smuzhiyun 380*4882a593Smuzhiyun def GetMakeArguments(self, board): 381*4882a593Smuzhiyun """Returns 'make' arguments for a given board 382*4882a593Smuzhiyun 383*4882a593Smuzhiyun The flags are in a section called 'make-flags'. Flags are named 384*4882a593Smuzhiyun after the target they represent, for example snapper9260=TESTING=1 385*4882a593Smuzhiyun will pass TESTING=1 to make when building the snapper9260 board. 386*4882a593Smuzhiyun 387*4882a593Smuzhiyun References to other boards can be added in the string also. For 388*4882a593Smuzhiyun example: 389*4882a593Smuzhiyun 390*4882a593Smuzhiyun [make-flags] 391*4882a593Smuzhiyun at91-boards=ENABLE_AT91_TEST=1 392*4882a593Smuzhiyun snapper9260=${at91-boards} BUILD_TAG=442 393*4882a593Smuzhiyun snapper9g45=${at91-boards} BUILD_TAG=443 394*4882a593Smuzhiyun 395*4882a593Smuzhiyun This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 396*4882a593Smuzhiyun and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. 397*4882a593Smuzhiyun 398*4882a593Smuzhiyun A special 'target' variable is set to the board target. 399*4882a593Smuzhiyun 400*4882a593Smuzhiyun Args: 401*4882a593Smuzhiyun board: Board object for the board to check. 402*4882a593Smuzhiyun Returns: 403*4882a593Smuzhiyun 'make' flags for that board, or '' if none 404*4882a593Smuzhiyun """ 405*4882a593Smuzhiyun self._make_flags['target'] = board.target 406*4882a593Smuzhiyun arg_str = self.ResolveReferences(self._make_flags, 407*4882a593Smuzhiyun self._make_flags.get(board.target, '')) 408*4882a593Smuzhiyun args = arg_str.split(' ') 409*4882a593Smuzhiyun i = 0 410*4882a593Smuzhiyun while i < len(args): 411*4882a593Smuzhiyun if not args[i]: 412*4882a593Smuzhiyun del args[i] 413*4882a593Smuzhiyun else: 414*4882a593Smuzhiyun i += 1 415*4882a593Smuzhiyun return args 416*4882a593Smuzhiyun 417*4882a593Smuzhiyun def LocateArchUrl(self, fetch_arch): 418*4882a593Smuzhiyun """Find a toolchain available online 419*4882a593Smuzhiyun 420*4882a593Smuzhiyun Look in standard places for available toolchains. At present the 421*4882a593Smuzhiyun only standard place is at kernel.org. 422*4882a593Smuzhiyun 423*4882a593Smuzhiyun Args: 424*4882a593Smuzhiyun arch: Architecture to look for, or 'list' for all 425*4882a593Smuzhiyun Returns: 426*4882a593Smuzhiyun If fetch_arch is 'list', a tuple: 427*4882a593Smuzhiyun Machine architecture (e.g. x86_64) 428*4882a593Smuzhiyun List of toolchains 429*4882a593Smuzhiyun else 430*4882a593Smuzhiyun URL containing this toolchain, if avaialble, else None 431*4882a593Smuzhiyun """ 432*4882a593Smuzhiyun arch = command.OutputOneLine('uname', '-m') 433*4882a593Smuzhiyun base = 'https://www.kernel.org/pub/tools/crosstool/files/bin' 434*4882a593Smuzhiyun versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4'] 435*4882a593Smuzhiyun links = [] 436*4882a593Smuzhiyun for version in versions: 437*4882a593Smuzhiyun url = '%s/%s/%s/' % (base, arch, version) 438*4882a593Smuzhiyun print 'Checking: %s' % url 439*4882a593Smuzhiyun response = urllib2.urlopen(url) 440*4882a593Smuzhiyun html = response.read() 441*4882a593Smuzhiyun parser = MyHTMLParser(fetch_arch) 442*4882a593Smuzhiyun parser.feed(html) 443*4882a593Smuzhiyun if fetch_arch == 'list': 444*4882a593Smuzhiyun links += parser.links 445*4882a593Smuzhiyun elif parser.arch_link: 446*4882a593Smuzhiyun return url + parser.arch_link 447*4882a593Smuzhiyun if fetch_arch == 'list': 448*4882a593Smuzhiyun return arch, links 449*4882a593Smuzhiyun return None 450*4882a593Smuzhiyun 451*4882a593Smuzhiyun def Download(self, url): 452*4882a593Smuzhiyun """Download a file to a temporary directory 453*4882a593Smuzhiyun 454*4882a593Smuzhiyun Args: 455*4882a593Smuzhiyun url: URL to download 456*4882a593Smuzhiyun Returns: 457*4882a593Smuzhiyun Tuple: 458*4882a593Smuzhiyun Temporary directory name 459*4882a593Smuzhiyun Full path to the downloaded archive file in that directory, 460*4882a593Smuzhiyun or None if there was an error while downloading 461*4882a593Smuzhiyun """ 462*4882a593Smuzhiyun print 'Downloading: %s' % url 463*4882a593Smuzhiyun leaf = url.split('/')[-1] 464*4882a593Smuzhiyun tmpdir = tempfile.mkdtemp('.buildman') 465*4882a593Smuzhiyun response = urllib2.urlopen(url) 466*4882a593Smuzhiyun fname = os.path.join(tmpdir, leaf) 467*4882a593Smuzhiyun fd = open(fname, 'wb') 468*4882a593Smuzhiyun meta = response.info() 469*4882a593Smuzhiyun size = int(meta.getheaders('Content-Length')[0]) 470*4882a593Smuzhiyun done = 0 471*4882a593Smuzhiyun block_size = 1 << 16 472*4882a593Smuzhiyun status = '' 473*4882a593Smuzhiyun 474*4882a593Smuzhiyun # Read the file in chunks and show progress as we go 475*4882a593Smuzhiyun while True: 476*4882a593Smuzhiyun buffer = response.read(block_size) 477*4882a593Smuzhiyun if not buffer: 478*4882a593Smuzhiyun print chr(8) * (len(status) + 1), '\r', 479*4882a593Smuzhiyun break 480*4882a593Smuzhiyun 481*4882a593Smuzhiyun done += len(buffer) 482*4882a593Smuzhiyun fd.write(buffer) 483*4882a593Smuzhiyun status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024, 484*4882a593Smuzhiyun done * 100 / size) 485*4882a593Smuzhiyun status = status + chr(8) * (len(status) + 1) 486*4882a593Smuzhiyun print status, 487*4882a593Smuzhiyun sys.stdout.flush() 488*4882a593Smuzhiyun fd.close() 489*4882a593Smuzhiyun if done != size: 490*4882a593Smuzhiyun print 'Error, failed to download' 491*4882a593Smuzhiyun os.remove(fname) 492*4882a593Smuzhiyun fname = None 493*4882a593Smuzhiyun return tmpdir, fname 494*4882a593Smuzhiyun 495*4882a593Smuzhiyun def Unpack(self, fname, dest): 496*4882a593Smuzhiyun """Unpack a tar file 497*4882a593Smuzhiyun 498*4882a593Smuzhiyun Args: 499*4882a593Smuzhiyun fname: Filename to unpack 500*4882a593Smuzhiyun dest: Destination directory 501*4882a593Smuzhiyun Returns: 502*4882a593Smuzhiyun Directory name of the first entry in the archive, without the 503*4882a593Smuzhiyun trailing / 504*4882a593Smuzhiyun """ 505*4882a593Smuzhiyun stdout = command.Output('tar', 'xvfJ', fname, '-C', dest) 506*4882a593Smuzhiyun return stdout.splitlines()[0][:-1] 507*4882a593Smuzhiyun 508*4882a593Smuzhiyun def TestSettingsHasPath(self, path): 509*4882a593Smuzhiyun """Check if buildman will find this toolchain 510*4882a593Smuzhiyun 511*4882a593Smuzhiyun Returns: 512*4882a593Smuzhiyun True if the path is in settings, False if not 513*4882a593Smuzhiyun """ 514*4882a593Smuzhiyun paths = self.GetPathList(False) 515*4882a593Smuzhiyun return path in paths 516*4882a593Smuzhiyun 517*4882a593Smuzhiyun def ListArchs(self): 518*4882a593Smuzhiyun """List architectures with available toolchains to download""" 519*4882a593Smuzhiyun host_arch, archives = self.LocateArchUrl('list') 520*4882a593Smuzhiyun re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*') 521*4882a593Smuzhiyun arch_set = set() 522*4882a593Smuzhiyun for archive in archives: 523*4882a593Smuzhiyun # Remove the host architecture from the start 524*4882a593Smuzhiyun arch = re_arch.match(archive[len(host_arch):]) 525*4882a593Smuzhiyun if arch: 526*4882a593Smuzhiyun arch_set.add(arch.group(1)) 527*4882a593Smuzhiyun return sorted(arch_set) 528*4882a593Smuzhiyun 529*4882a593Smuzhiyun def FetchAndInstall(self, arch): 530*4882a593Smuzhiyun """Fetch and install a new toolchain 531*4882a593Smuzhiyun 532*4882a593Smuzhiyun arch: 533*4882a593Smuzhiyun Architecture to fetch, or 'list' to list 534*4882a593Smuzhiyun """ 535*4882a593Smuzhiyun # Fist get the URL for this architecture 536*4882a593Smuzhiyun col = terminal.Color() 537*4882a593Smuzhiyun print col.Color(col.BLUE, "Downloading toolchain for arch '%s'" % arch) 538*4882a593Smuzhiyun url = self.LocateArchUrl(arch) 539*4882a593Smuzhiyun if not url: 540*4882a593Smuzhiyun print ("Cannot find toolchain for arch '%s' - use 'list' to list" % 541*4882a593Smuzhiyun arch) 542*4882a593Smuzhiyun return 2 543*4882a593Smuzhiyun home = os.environ['HOME'] 544*4882a593Smuzhiyun dest = os.path.join(home, '.buildman-toolchains') 545*4882a593Smuzhiyun if not os.path.exists(dest): 546*4882a593Smuzhiyun os.mkdir(dest) 547*4882a593Smuzhiyun 548*4882a593Smuzhiyun # Download the tar file for this toolchain and unpack it 549*4882a593Smuzhiyun tmpdir, tarfile = self.Download(url) 550*4882a593Smuzhiyun if not tarfile: 551*4882a593Smuzhiyun return 1 552*4882a593Smuzhiyun print col.Color(col.GREEN, 'Unpacking to: %s' % dest), 553*4882a593Smuzhiyun sys.stdout.flush() 554*4882a593Smuzhiyun path = self.Unpack(tarfile, dest) 555*4882a593Smuzhiyun os.remove(tarfile) 556*4882a593Smuzhiyun os.rmdir(tmpdir) 557*4882a593Smuzhiyun print 558*4882a593Smuzhiyun 559*4882a593Smuzhiyun # Check that the toolchain works 560*4882a593Smuzhiyun print col.Color(col.GREEN, 'Testing') 561*4882a593Smuzhiyun dirpath = os.path.join(dest, path) 562*4882a593Smuzhiyun compiler_fname_list = self.ScanPath(dirpath, True) 563*4882a593Smuzhiyun if not compiler_fname_list: 564*4882a593Smuzhiyun print 'Could not locate C compiler - fetch failed.' 565*4882a593Smuzhiyun return 1 566*4882a593Smuzhiyun if len(compiler_fname_list) != 1: 567*4882a593Smuzhiyun print col.Color(col.RED, 'Warning, ambiguous toolchains: %s' % 568*4882a593Smuzhiyun ', '.join(compiler_fname_list)) 569*4882a593Smuzhiyun toolchain = Toolchain(compiler_fname_list[0], True, True) 570*4882a593Smuzhiyun 571*4882a593Smuzhiyun # Make sure that it will be found by buildman 572*4882a593Smuzhiyun if not self.TestSettingsHasPath(dirpath): 573*4882a593Smuzhiyun print ("Adding 'download' to config file '%s'" % 574*4882a593Smuzhiyun bsettings.config_fname) 575*4882a593Smuzhiyun bsettings.SetItem('toolchain', 'download', '%s/*/*' % dest) 576*4882a593Smuzhiyun return 0 577