]>
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) | |
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: | |
8bb2bddc SW |
42 | result = command.RunPipe([cmd], capture=True, env=env, |
43 | raise_on_error=False) | |
fc3fe1c2 SG |
44 | 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 | """ | |
8708267f | 69 | priority_list = ['-elf', '-unknown-linux-gnu', '-linux', |
fc3fe1c2 SG |
70 | '-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 | ||
89 | class 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 = [] | |
4281ad8e SG |
102 | 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" % | |
1826a18d | 106 | bsettings.config_fname) |
4281ad8e SG |
107 | |
108 | for name, value in toolchains: | |
fc3fe1c2 SG |
109 | if '*' in value: |
110 | self.paths += glob.glob(value) | |
111 | else: | |
112 | self.paths.append(value) | |
4281ad8e | 113 | self._make_flags = dict(bsettings.GetItems('make-flags')) |
fc3fe1c2 SG |
114 | |
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] | |
4281ad8e SG |
178 | |
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 |