blob: a292338b6d0894bd4cd5ca78154bd3c4c9b77b66 [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:
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 = []
Simon Glass4281ad82013-09-23 17:35:17 -0600101 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:
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000108 if '*' in value:
109 self.paths += glob.glob(value)
110 else:
111 self.paths.append(value)
Simon Glass4281ad82013-09-23 17:35:17 -0600112 self._make_flags = dict(bsettings.GetItems('make-flags'))
Simon Glassfc3fe1c2013-04-03 11:07:16 +0000113
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]
Simon Glass4281ad82013-09-23 17:35:17 -0600177
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