blob: 27dc31889b8021c64d0af0d9bdf2898253e0b993 [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 Glassd4144e42014-09-05 19:00:13 -0600102 self._make_flags = dict(bsettings.GetItems('make-flags'))
103
104 def GetSettings(self):
Simon Glass4281ad82013-09-23 17:35:17 -0600105 toolchains = bsettings.GetItems('toolchain')
106 if not toolchains:
107 print ("Warning: No tool chains - please add a [toolchain] section"
108 " to your buildman config file %s. See README for details" %
Masahiro Yamada1826a182014-07-07 09:46:36 +0900109 bsettings.config_fname)
Simon Glass4281ad82013-09-23 17:35:17 -0600110
111 for name, value in toolchains:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000112 if '*' in value:
113 self.paths += glob.glob(value)
114 else:
115 self.paths.append(value)
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000116
117 def Add(self, fname, test=True, verbose=False):
118 """Add a toolchain to our list
119
120 We select the given toolchain as our preferred one for its
121 architecture if it is a higher priority than the others.
122
123 Args:
124 fname: Filename of toolchain's gcc driver
125 test: True to run the toolchain to test it
126 """
127 toolchain = Toolchain(fname, test, verbose)
128 add_it = toolchain.ok
129 if toolchain.arch in self.toolchains:
130 add_it = (toolchain.priority <
131 self.toolchains[toolchain.arch].priority)
132 if add_it:
133 self.toolchains[toolchain.arch] = toolchain
134
135 def Scan(self, verbose):
136 """Scan for available toolchains and select the best for each arch.
137
138 We look for all the toolchains we can file, figure out the
139 architecture for each, and whether it works. Then we select the
140 highest priority toolchain for each arch.
141
142 Args:
143 verbose: True to print out progress information
144 """
145 if verbose: print 'Scanning for tool chains'
146 for path in self.paths:
147 if verbose: print " - scanning path '%s'" % path
148 for subdir in ['.', 'bin', 'usr/bin']:
149 dirname = os.path.join(path, subdir)
150 if verbose: print " - looking in '%s'" % dirname
151 for fname in glob.glob(dirname + '/*gcc'):
152 if verbose: print " - found '%s'" % fname
153 self.Add(fname, True, verbose)
154
155 def List(self):
156 """List out the selected toolchains for each architecture"""
157 print 'List of available toolchains (%d):' % len(self.toolchains)
158 if len(self.toolchains):
159 for key, value in sorted(self.toolchains.iteritems()):
160 print '%-10s: %s' % (key, value.gcc)
161 else:
162 print 'None'
163
164 def Select(self, arch):
165 """Returns the toolchain for a given architecture
166
167 Args:
168 args: Name of architecture (e.g. 'arm', 'ppc_8xx')
169
170 returns:
171 toolchain object, or None if none found
172 """
173 for name, value in bsettings.GetItems('toolchain-alias'):
174 if arch == name:
175 arch = value
176
177 if not arch in self.toolchains:
178 raise ValueError, ("No tool chain found for arch '%s'" % arch)
179 return self.toolchains[arch]
Simon Glass4281ad82013-09-23 17:35:17 -0600180
181 def ResolveReferences(self, var_dict, args):
182 """Resolve variable references in a string
183
184 This converts ${blah} within the string to the value of blah.
185 This function works recursively.
186
187 Args:
188 var_dict: Dictionary containing variables and their values
189 args: String containing make arguments
190 Returns:
191 Resolved string
192
193 >>> bsettings.Setup()
194 >>> tcs = Toolchains()
195 >>> tcs.Add('fred', False)
196 >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
197 'second' : '2nd'}
198 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
199 'this=OBLIQUE_set'
200 >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
201 'this=OBLIQUE_setfi2ndrstnd'
202 """
Simon Glassf60c9d42014-08-28 09:43:40 -0600203 re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
Simon Glass4281ad82013-09-23 17:35:17 -0600204
205 while True:
206 m = re_var.search(args)
207 if not m:
208 break
209 lookup = m.group(0)[2:-1]
210 value = var_dict.get(lookup, '')
211 args = args[:m.start(0)] + value + args[m.end(0):]
212 return args
213
214 def GetMakeArguments(self, board):
215 """Returns 'make' arguments for a given board
216
217 The flags are in a section called 'make-flags'. Flags are named
218 after the target they represent, for example snapper9260=TESTING=1
219 will pass TESTING=1 to make when building the snapper9260 board.
220
221 References to other boards can be added in the string also. For
222 example:
223
224 [make-flags]
225 at91-boards=ENABLE_AT91_TEST=1
226 snapper9260=${at91-boards} BUILD_TAG=442
227 snapper9g45=${at91-boards} BUILD_TAG=443
228
229 This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
230 and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
231
232 A special 'target' variable is set to the board target.
233
234 Args:
235 board: Board object for the board to check.
236 Returns:
237 'make' flags for that board, or '' if none
238 """
239 self._make_flags['target'] = board.target
240 arg_str = self.ResolveReferences(self._make_flags,
241 self._make_flags.get(board.target, ''))
242 args = arg_str.split(' ')
243 i = 0
244 while i < len(args):
245 if not args[i]:
246 del args[i]
247 else:
248 i += 1
249 return args