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