blob: 1b9771f8e6f42a749a8be0e50e2a327252bdf8e7 [file] [log] [blame]
Simon Glassfc3fe1c2013-04-03 11:07:16 +00001# Copyright (c) 2012 The Chromium OS Authors.
2#
Wolfgang Denk1a459662013-07-08 09:37:19 +02003# SPDX-License-Identifier: GPL-2.0+
Simon Glassfc3fe1c2013-04-03 11:07:16 +00004#
5
Simon Glass4281ad82013-09-23 17:35:17 -06006import re
Simon Glassfc3fe1c2013-04-03 11:07:16 +00007import 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:
Stephen Warren8bb2bdd2013-10-09 14:28:09 -060042 result = command.RunPipe([cmd], capture=True, env=env,
43 raise_on_error=False)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000044 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 """
Masahiro Yamada87082672014-07-07 09:47:45 +090069 priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
Simon Glassfc3fe1c2013-04-03 11:07:16 +000070 '-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 = []
Simon Glass4281ad82013-09-23 17:35:17 -0600102 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" %
Masahiro Yamada1826a182014-07-07 09:46:36 +0900106 bsettings.config_fname)
Simon Glass4281ad82013-09-23 17:35:17 -0600107
108 for name, value in toolchains:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000109 if '*' in value:
110 self.paths += glob.glob(value)
111 else:
112 self.paths.append(value)
Simon Glass4281ad82013-09-23 17:35:17 -0600113 self._make_flags = dict(bsettings.GetItems('make-flags'))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000114
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]
Simon Glass4281ad82013-09-23 17:35:17 -0600178
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