1# Copyright (c) 2012 The Chromium OS Authors. 2# 3# SPDX-License-Identifier: GPL-2.0+ 4# 5 6import re 7import glob 8import os 9 10import bsettings 11import command 12 13class Toolchain: 14 """A single toolchain 15 16 Public members: 17 gcc: Full path to C compiler 18 path: Directory path containing C compiler 19 cross: Cross compile string, e.g. 'arm-linux-' 20 arch: Architecture of toolchain as determined from the first 21 component of the filename. E.g. arm-linux-gcc becomes arm 22 """ 23 24 def __init__(self, fname, test, verbose=False): 25 """Create a new toolchain object. 26 27 Args: 28 fname: Filename of the gcc component 29 test: True to run the toolchain to test it 30 """ 31 self.gcc = fname 32 self.path = os.path.dirname(fname) 33 self.cross = os.path.basename(fname)[:-3] 34 pos = self.cross.find('-') 35 self.arch = self.cross[:pos] if pos != -1 else 'sandbox' 36 37 env = self.MakeEnvironment() 38 39 # As a basic sanity check, run the C compiler with --version 40 cmd = [fname, '--version'] 41 if test: 42 result = command.RunPipe([cmd], capture=True, env=env, 43 raise_on_error=False) 44 self.ok = result.return_code == 0 45 if verbose: 46 print 'Tool chain test: ', 47 if self.ok: 48 print 'OK' 49 else: 50 print 'BAD' 51 print 'Command: ', cmd 52 print result.stdout 53 print result.stderr 54 else: 55 self.ok = True 56 self.priority = self.GetPriority(fname) 57 58 def GetPriority(self, fname): 59 """Return the priority of the toolchain. 60 61 Toolchains are ranked according to their suitability by their 62 filename prefix. 63 64 Args: 65 fname: Filename of toolchain 66 Returns: 67 Priority of toolchain, 0=highest, 20=lowest. 68 """ 69 priority_list = ['-elf', '-unknown-linux-gnu', '-linux', 70 '-none-linux-gnueabi', '-uclinux', '-none-eabi', 71 '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux'] 72 for prio in range(len(priority_list)): 73 if priority_list[prio] in fname: 74 return prio 75 return prio 76 77 def MakeEnvironment(self): 78 """Returns an environment for using the toolchain. 79 80 Thie takes the current environment, adds CROSS_COMPILE and 81 augments PATH so that the toolchain will operate correctly. 82 """ 83 env = dict(os.environ) 84 env['CROSS_COMPILE'] = self.cross 85 env['PATH'] += (':' + self.path) 86 return env 87 88 89class Toolchains: 90 """Manage a list of toolchains for building U-Boot 91 92 We select one toolchain for each architecture type 93 94 Public members: 95 toolchains: Dict of Toolchain objects, keyed by architecture name 96 paths: List of paths to check for toolchains (may contain wildcards) 97 """ 98 99 def __init__(self): 100 self.toolchains = {} 101 self.paths = [] 102 self._make_flags = dict(bsettings.GetItems('make-flags')) 103 104 def GetSettings(self): 105 toolchains = bsettings.GetItems('toolchain') 106 if not toolchains: 107 print ("Warning: No tool chains - please add a [toolchain] section" 108 " to your buildman config file %s. See README for details" % 109 bsettings.config_fname) 110 111 for name, value in toolchains: 112 if '*' in value: 113 self.paths += glob.glob(value) 114 else: 115 self.paths.append(value) 116 117 def Add(self, fname, test=True, verbose=False): 118 """Add a toolchain to our list 119 120 We select the given toolchain as our preferred one for its 121 architecture if it is a higher priority than the others. 122 123 Args: 124 fname: Filename of toolchain's gcc driver 125 test: True to run the toolchain to test it 126 """ 127 toolchain = Toolchain(fname, test, verbose) 128 add_it = toolchain.ok 129 if toolchain.arch in self.toolchains: 130 add_it = (toolchain.priority < 131 self.toolchains[toolchain.arch].priority) 132 if add_it: 133 self.toolchains[toolchain.arch] = toolchain 134 135 def Scan(self, verbose): 136 """Scan for available toolchains and select the best for each arch. 137 138 We look for all the toolchains we can file, figure out the 139 architecture for each, and whether it works. Then we select the 140 highest priority toolchain for each arch. 141 142 Args: 143 verbose: True to print out progress information 144 """ 145 if verbose: print 'Scanning for tool chains' 146 for path in self.paths: 147 if verbose: print " - scanning path '%s'" % path 148 for subdir in ['.', 'bin', 'usr/bin']: 149 dirname = os.path.join(path, subdir) 150 if verbose: print " - looking in '%s'" % dirname 151 for fname in glob.glob(dirname + '/*gcc'): 152 if verbose: print " - found '%s'" % fname 153 self.Add(fname, True, verbose) 154 155 def List(self): 156 """List out the selected toolchains for each architecture""" 157 print 'List of available toolchains (%d):' % len(self.toolchains) 158 if len(self.toolchains): 159 for key, value in sorted(self.toolchains.iteritems()): 160 print '%-10s: %s' % (key, value.gcc) 161 else: 162 print 'None' 163 164 def Select(self, arch): 165 """Returns the toolchain for a given architecture 166 167 Args: 168 args: Name of architecture (e.g. 'arm', 'ppc_8xx') 169 170 returns: 171 toolchain object, or None if none found 172 """ 173 for name, value in bsettings.GetItems('toolchain-alias'): 174 if arch == name: 175 arch = value 176 177 if not arch in self.toolchains: 178 raise ValueError, ("No tool chain found for arch '%s'" % arch) 179 return self.toolchains[arch] 180 181 def ResolveReferences(self, var_dict, args): 182 """Resolve variable references in a string 183 184 This converts ${blah} within the string to the value of blah. 185 This function works recursively. 186 187 Args: 188 var_dict: Dictionary containing variables and their values 189 args: String containing make arguments 190 Returns: 191 Resolved string 192 193 >>> bsettings.Setup() 194 >>> tcs = Toolchains() 195 >>> tcs.Add('fred', False) 196 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ 197 'second' : '2nd'} 198 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') 199 'this=OBLIQUE_set' 200 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') 201 'this=OBLIQUE_setfi2ndrstnd' 202 """ 203 re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') 204 205 while True: 206 m = re_var.search(args) 207 if not m: 208 break 209 lookup = m.group(0)[2:-1] 210 value = var_dict.get(lookup, '') 211 args = args[:m.start(0)] + value + args[m.end(0):] 212 return args 213 214 def GetMakeArguments(self, board): 215 """Returns 'make' arguments for a given board 216 217 The flags are in a section called 'make-flags'. Flags are named 218 after the target they represent, for example snapper9260=TESTING=1 219 will pass TESTING=1 to make when building the snapper9260 board. 220 221 References to other boards can be added in the string also. For 222 example: 223 224 [make-flags] 225 at91-boards=ENABLE_AT91_TEST=1 226 snapper9260=${at91-boards} BUILD_TAG=442 227 snapper9g45=${at91-boards} BUILD_TAG=443 228 229 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 230 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. 231 232 A special 'target' variable is set to the board target. 233 234 Args: 235 board: Board object for the board to check. 236 Returns: 237 'make' flags for that board, or '' if none 238 """ 239 self._make_flags['target'] = board.target 240 arg_str = self.ResolveReferences(self._make_flags, 241 self._make_flags.get(board.target, '')) 242 args = arg_str.split(' ') 243 i = 0 244 while i < len(args): 245 if not args[i]: 246 del args[i] 247 else: 248 i += 1 249 return args 250