blob: ad4df8cc9bacfc6d87fc42a9b95c7f3c7cef18f9 [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)
Simon Glassb5324122014-12-01 17:33:58 -070033
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
Simon Glassfc3fe1c2013-04-03 11:07:16 +000041 pos = self.cross.find('-')
42 self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
43
Simon Glassbb1501f2014-12-01 17:34:00 -070044 env = self.MakeEnvironment(False)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000045
46 # As a basic sanity check, run the C compiler with --version
47 cmd = [fname, '--version']
48 if test:
Stephen Warren8bb2bdd2013-10-09 14:28:09 -060049 result = command.RunPipe([cmd], capture=True, env=env,
50 raise_on_error=False)
Simon Glassfc3fe1c2013-04-03 11:07:16 +000051 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 """
Masahiro Yamada87082672014-07-07 09:47:45 +090076 priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
Simon Glassfc3fe1c2013-04-03 11:07:16 +000077 '-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
Simon Glassbb1501f2014-12-01 17:34:00 -070084 def MakeEnvironment(self, full_path):
Simon Glassfc3fe1c2013-04-03 11:07:16 +000085 """Returns an environment for using the toolchain.
86
Simon Glassbb1501f2014-12-01 17:34:00 -070087 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
Simon Glassfc3fe1c2013-04-03 11:07:16 +000093 """
94 env = dict(os.environ)
Simon Glassbb1501f2014-12-01 17:34:00 -070095 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
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000101 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 = []
Simon Glassd4144e42014-09-05 19:00:13 -0600117 self._make_flags = dict(bsettings.GetItems('make-flags'))
118
119 def GetSettings(self):
Simon Glass4281ad82013-09-23 17:35:17 -0600120 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" %
Masahiro Yamada1826a182014-07-07 09:46:36 +0900124 bsettings.config_fname)
Simon Glass4281ad82013-09-23 17:35:17 -0600125
126 for name, value in toolchains:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000127 if '*' in value:
128 self.paths += glob.glob(value)
129 else:
130 self.paths.append(value)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000131
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 """
Simon Glass9b83bfd2014-12-01 17:34:05 -0700188 for tag, value in bsettings.GetItems('toolchain-alias'):
189 if arch == tag:
190 for alias in value.split():
191 if alias in self.toolchains:
192 return self.toolchains[alias]
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000193
194 if not arch in self.toolchains:
195 raise ValueError, ("No tool chain found for arch '%s'" % arch)
196 return self.toolchains[arch]
Simon Glass4281ad82013-09-23 17:35:17 -0600197
198 def ResolveReferences(self, var_dict, args):
199 """Resolve variable references in a string
200
201 This converts ${blah} within the string to the value of blah.
202 This function works recursively.
203
204 Args:
205 var_dict: Dictionary containing variables and their values
206 args: String containing make arguments
207 Returns:
208 Resolved string
209
210 >>> bsettings.Setup()
211 >>> tcs = Toolchains()
212 >>> tcs.Add('fred', False)
213 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
214 'second' : '2nd'}
215 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
216 'this=OBLIQUE_set'
217 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
218 'this=OBLIQUE_setfi2ndrstnd'
219 """
Simon Glassf60c9d42014-08-28 09:43:40 -0600220 re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
Simon Glass4281ad82013-09-23 17:35:17 -0600221
222 while True:
223 m = re_var.search(args)
224 if not m:
225 break
226 lookup = m.group(0)[2:-1]
227 value = var_dict.get(lookup, '')
228 args = args[:m.start(0)] + value + args[m.end(0):]
229 return args
230
231 def GetMakeArguments(self, board):
232 """Returns 'make' arguments for a given board
233
234 The flags are in a section called 'make-flags'. Flags are named
235 after the target they represent, for example snapper9260=TESTING=1
236 will pass TESTING=1 to make when building the snapper9260 board.
237
238 References to other boards can be added in the string also. For
239 example:
240
241 [make-flags]
242 at91-boards=ENABLE_AT91_TEST=1
243 snapper9260=${at91-boards} BUILD_TAG=442
244 snapper9g45=${at91-boards} BUILD_TAG=443
245
246 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
247 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
248
249 A special 'target' variable is set to the board target.
250
251 Args:
252 board: Board object for the board to check.
253 Returns:
254 'make' flags for that board, or '' if none
255 """
256 self._make_flags['target'] = board.target
257 arg_str = self.ResolveReferences(self._make_flags,
258 self._make_flags.get(board.target, ''))
259 args = arg_str.split(' ')
260 i = 0
261 while i < len(args):
262 if not args[i]:
263 del args[i]
264 else:
265 i += 1
266 return args