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