]>
Commit | Line | Data |
---|---|---|
fc3fe1c2 SG |
1 | # Copyright (c) 2012 The Chromium OS Authors. |
2 | # | |
1a459660 | 3 | # SPDX-License-Identifier: GPL-2.0+ |
fc3fe1c2 SG |
4 | # |
5 | ||
4281ad8e | 6 | import re |
fc3fe1c2 SG |
7 | import glob |
8 | import os | |
9 | ||
10 | import bsettings | |
11 | import command | |
12 | ||
13 | class 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) | |
b5324123 SG |
33 | |
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 | |
fc3fe1c2 SG |
41 | pos = self.cross.find('-') |
42 | self.arch = self.cross[:pos] if pos != -1 else 'sandbox' | |
43 | ||
44 | env = self.MakeEnvironment() | |
45 | ||
46 | # As a basic sanity check, run the C compiler with --version | |
47 | cmd = [fname, '--version'] | |
48 | if test: | |
8bb2bddc SW |
49 | result = command.RunPipe([cmd], capture=True, env=env, |
50 | raise_on_error=False) | |
fc3fe1c2 SG |
51 | 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 | """ | |
8708267f | 76 | priority_list = ['-elf', '-unknown-linux-gnu', '-linux', |
fc3fe1c2 SG |
77 | '-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 | ||
84 | def MakeEnvironment(self): | |
85 | """Returns an environment for using the toolchain. | |
86 | ||
87 | Thie takes the current environment, adds CROSS_COMPILE and | |
88 | augments PATH so that the toolchain will operate correctly. | |
89 | """ | |
90 | env = dict(os.environ) | |
91 | env['CROSS_COMPILE'] = self.cross | |
92 | env['PATH'] += (':' + self.path) | |
93 | return env | |
94 | ||
95 | ||
96 | class Toolchains: | |
97 | """Manage a list of toolchains for building U-Boot | |
98 | ||
99 | We select one toolchain for each architecture type | |
100 | ||
101 | Public members: | |
102 | toolchains: Dict of Toolchain objects, keyed by architecture name | |
103 | paths: List of paths to check for toolchains (may contain wildcards) | |
104 | """ | |
105 | ||
106 | def __init__(self): | |
107 | self.toolchains = {} | |
108 | self.paths = [] | |
d4144e45 SG |
109 | self._make_flags = dict(bsettings.GetItems('make-flags')) |
110 | ||
111 | def GetSettings(self): | |
4281ad8e SG |
112 | toolchains = bsettings.GetItems('toolchain') |
113 | if not toolchains: | |
114 | print ("Warning: No tool chains - please add a [toolchain] section" | |
115 | " to your buildman config file %s. See README for details" % | |
1826a18d | 116 | bsettings.config_fname) |
4281ad8e SG |
117 | |
118 | for name, value in toolchains: | |
fc3fe1c2 SG |
119 | if '*' in value: |
120 | self.paths += glob.glob(value) | |
121 | else: | |
122 | self.paths.append(value) | |
fc3fe1c2 SG |
123 | |
124 | def Add(self, fname, test=True, verbose=False): | |
125 | """Add a toolchain to our list | |
126 | ||
127 | We select the given toolchain as our preferred one for its | |
128 | architecture if it is a higher priority than the others. | |
129 | ||
130 | Args: | |
131 | fname: Filename of toolchain's gcc driver | |
132 | test: True to run the toolchain to test it | |
133 | """ | |
134 | toolchain = Toolchain(fname, test, verbose) | |
135 | add_it = toolchain.ok | |
136 | if toolchain.arch in self.toolchains: | |
137 | add_it = (toolchain.priority < | |
138 | self.toolchains[toolchain.arch].priority) | |
139 | if add_it: | |
140 | self.toolchains[toolchain.arch] = toolchain | |
141 | ||
142 | def Scan(self, verbose): | |
143 | """Scan for available toolchains and select the best for each arch. | |
144 | ||
145 | We look for all the toolchains we can file, figure out the | |
146 | architecture for each, and whether it works. Then we select the | |
147 | highest priority toolchain for each arch. | |
148 | ||
149 | Args: | |
150 | verbose: True to print out progress information | |
151 | """ | |
152 | if verbose: print 'Scanning for tool chains' | |
153 | for path in self.paths: | |
154 | if verbose: print " - scanning path '%s'" % path | |
155 | for subdir in ['.', 'bin', 'usr/bin']: | |
156 | dirname = os.path.join(path, subdir) | |
157 | if verbose: print " - looking in '%s'" % dirname | |
158 | for fname in glob.glob(dirname + '/*gcc'): | |
159 | if verbose: print " - found '%s'" % fname | |
160 | self.Add(fname, True, verbose) | |
161 | ||
162 | def List(self): | |
163 | """List out the selected toolchains for each architecture""" | |
164 | print 'List of available toolchains (%d):' % len(self.toolchains) | |
165 | if len(self.toolchains): | |
166 | for key, value in sorted(self.toolchains.iteritems()): | |
167 | print '%-10s: %s' % (key, value.gcc) | |
168 | else: | |
169 | print 'None' | |
170 | ||
171 | def Select(self, arch): | |
172 | """Returns the toolchain for a given architecture | |
173 | ||
174 | Args: | |
175 | args: Name of architecture (e.g. 'arm', 'ppc_8xx') | |
176 | ||
177 | returns: | |
178 | toolchain object, or None if none found | |
179 | """ | |
180 | for name, value in bsettings.GetItems('toolchain-alias'): | |
181 | if arch == name: | |
182 | arch = value | |
183 | ||
184 | if not arch in self.toolchains: | |
185 | raise ValueError, ("No tool chain found for arch '%s'" % arch) | |
186 | return self.toolchains[arch] | |
4281ad8e SG |
187 | |
188 | def ResolveReferences(self, var_dict, args): | |
189 | """Resolve variable references in a string | |
190 | ||
191 | This converts ${blah} within the string to the value of blah. | |
192 | This function works recursively. | |
193 | ||
194 | Args: | |
195 | var_dict: Dictionary containing variables and their values | |
196 | args: String containing make arguments | |
197 | Returns: | |
198 | Resolved string | |
199 | ||
200 | >>> bsettings.Setup() | |
201 | >>> tcs = Toolchains() | |
202 | >>> tcs.Add('fred', False) | |
203 | >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \ | |
204 | 'second' : '2nd'} | |
205 | >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set') | |
206 | 'this=OBLIQUE_set' | |
207 | >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') | |
208 | 'this=OBLIQUE_setfi2ndrstnd' | |
209 | """ | |
f60c9d4f | 210 | re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') |
4281ad8e SG |
211 | |
212 | while True: | |
213 | m = re_var.search(args) | |
214 | if not m: | |
215 | break | |
216 | lookup = m.group(0)[2:-1] | |
217 | value = var_dict.get(lookup, '') | |
218 | args = args[:m.start(0)] + value + args[m.end(0):] | |
219 | return args | |
220 | ||
221 | def GetMakeArguments(self, board): | |
222 | """Returns 'make' arguments for a given board | |
223 | ||
224 | The flags are in a section called 'make-flags'. Flags are named | |
225 | after the target they represent, for example snapper9260=TESTING=1 | |
226 | will pass TESTING=1 to make when building the snapper9260 board. | |
227 | ||
228 | References to other boards can be added in the string also. For | |
229 | example: | |
230 | ||
231 | [make-flags] | |
232 | at91-boards=ENABLE_AT91_TEST=1 | |
233 | snapper9260=${at91-boards} BUILD_TAG=442 | |
234 | snapper9g45=${at91-boards} BUILD_TAG=443 | |
235 | ||
236 | This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260 | |
237 | and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45. | |
238 | ||
239 | A special 'target' variable is set to the board target. | |
240 | ||
241 | Args: | |
242 | board: Board object for the board to check. | |
243 | Returns: | |
244 | 'make' flags for that board, or '' if none | |
245 | """ | |
246 | self._make_flags['target'] = board.target | |
247 | arg_str = self.ResolveReferences(self._make_flags, | |
248 | self._make_flags.get(board.target, '')) | |
249 | args = arg_str.split(' ') | |
250 | i = 0 | |
251 | while i < len(args): | |
252 | if not args[i]: | |
253 | del args[i] | |
254 | else: | |
255 | i += 1 | |
256 | return args |