]> git.ipfire.org Git - people/ms/u-boot.git/blame - tools/buildman/toolchain.py
buildman: Try to avoid hard-coded string parsing
[people/ms/u-boot.git] / tools / buildman / toolchain.py
CommitLineData
fc3fe1c2
SG
1# Copyright (c) 2012 The Chromium OS Authors.
2#
1a459660 3# SPDX-License-Identifier: GPL-2.0+
fc3fe1c2
SG
4#
5
4281ad8e 6import re
fc3fe1c2
SG
7import 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)
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
96class 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