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