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