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', '-elf', 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 toolchains = bsettings.GetItems('toolchain') 103 if not toolchains: 104 print ("Warning: No tool chains - please add a [toolchain] section" 105 " to your buildman config file %s. See README for details" % 106 config_fname) 107 108 for name, value in toolchains: 109 if '*' in value: 110 self.paths += glob.glob(value) 111 else: 112 self.paths.append(value) 113 self._make_flags = dict(bsettings.GetItems('make-flags')) 114 115 def Add(self, fname, test=True, verbose=False): 116 """Add a toolchain to our list 117 118 We select the given toolchain as our preferred one for its 119 architecture if it is a higher priority than the others. 120 121 Args: 122 fname: Filename of toolchain's gcc driver 123 test: True to run the toolchain to test it 124 """ 125 toolchain = Toolchain(fname, test, verbose) 126 add_it = toolchain.ok 127 if toolchain.arch in self.toolchains: 128 add_it = (toolchain.priority < 129 self.toolchains[toolchain.arch].priority) 130 if add_it: 131 self.toolchains[toolchain.arch] = toolchain 132 133 def Scan(self, verbose): 134 """Scan for available toolchains and select the best for each arch. 135 136 We look for all the toolchains we can file, figure out the 137 architecture for each, and whether it works. Then we select the 138 highest priority toolchain for each arch. 139 140 Args: 141 verbose: True to print out progress information 142 """ 143 if verbose: print 'Scanning for tool chains' 144 for path in self.paths: 145 if verbose: print " - scanning path '%s'" % path 146 for subdir in ['.', 'bin', 'usr/bin']: 147 dirname = os.path.join(path, subdir) 148 if verbose: print " - looking in '%s'" % dirname 149 for fname in glob.glob(dirname + '/*gcc'): 150 if verbose: print " - found '%s'" % fname 151 self.Add(fname, True, verbose) 152 153 def List(self): 154 """List out the selected toolchains for each architecture""" 155 print 'List of available toolchains (%d):' % len(self.toolchains) 156 if len(self.toolchains): 157 for key, value in sorted(self.toolchains.iteritems()): 158 print '%-10s: %s' % (key, value.gcc) 159 else: 160 print 'None' 161 162 def Select(self, arch): 163 """Returns the toolchain for a given architecture 164 165 Args: 166 args: Name of architecture (e.g. 'arm', 'ppc_8xx') 167 168 returns: 169 toolchain object, or None if none found 170 """ 171 for name, value in bsettings.GetItems('toolchain-alias'): 172 if arch == name: 173 arch = value 174 175 if not arch in self.toolchains: 176 raise ValueError, ("No tool chain found for arch '%s'" % arch) 177 return self.toolchains[arch] 178 179 def ResolveReferences(self, var_dict, args): 180 """Resolve variable references in a string 181 182 This converts ${blah} within the string to the value of blah. 183 This function works recursively. 184 185 Args: 186 var_dict: Dictionary containing variables and their values 187 args: String containing make arguments 188 Returns: 189 Resolved string 190 191 >>> bsettings.Setup() 192 >>> tcs = Toolchains() 193 >>> tcs.Add('fred', False) 194 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ 195 'second' : '2nd'} 196 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') 197 'this=OBLIQUE_set' 198 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') 199 'this=OBLIQUE_setfi2ndrstnd' 200 """ 201 re_var = re.compile('(\$\{[a-z0-9A-Z]{1,}\})') 202 203 while True: 204 m = re_var.search(args) 205 if not m: 206 break 207 lookup = m.group(0)[2:-1] 208 value = var_dict.get(lookup, '') 209 args = args[:m.start(0)] + value + args[m.end(0):] 210 return args 211 212 def GetMakeArguments(self, board): 213 """Returns 'make' arguments for a given board 214 215 The flags are in a section called 'make-flags'. Flags are named 216 after the target they represent, for example snapper9260=TESTING=1 217 will pass TESTING=1 to make when building the snapper9260 board. 218 219 References to other boards can be added in the string also. For 220 example: 221 222 [make-flags] 223 at91-boards=ENABLE_AT91_TEST=1 224 snapper9260=${at91-boards} BUILD_TAG=442 225 snapper9g45=${at91-boards} BUILD_TAG=443 226 227 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 228 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. 229 230 A special 'target' variable is set to the board target. 231 232 Args: 233 board: Board object for the board to check. 234 Returns: 235 'make' flags for that board, or '' if none 236 """ 237 self._make_flags['target'] = board.target 238 arg_str = self.ResolveReferences(self._make_flags, 239 self._make_flags.get(board.target, '')) 240 args = arg_str.split(' ') 241 i = 0 242 while i < len(args): 243 if not args[i]: 244 del args[i] 245 else: 246 i += 1 247 return args 248