]> git.sur5r.net Git - u-boot/blob - tools/buildman/toolchain.py
buildman: Don't complain about missing sections in ~/.buildman
[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 name, value in bsettings.GetItems('toolchain-alias'):
189             if arch == name:
190                 arch = value
191
192         if not arch in self.toolchains:
193             raise ValueError, ("No tool chain found for arch '%s'" % arch)
194         return self.toolchains[arch]
195
196     def ResolveReferences(self, var_dict, args):
197         """Resolve variable references in a string
198
199         This converts ${blah} within the string to the value of blah.
200         This function works recursively.
201
202         Args:
203             var_dict: Dictionary containing variables and their values
204             args: String containing make arguments
205         Returns:
206             Resolved string
207
208         >>> bsettings.Setup()
209         >>> tcs = Toolchains()
210         >>> tcs.Add('fred', False)
211         >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
212                         'second' : '2nd'}
213         >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
214         'this=OBLIQUE_set'
215         >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
216         'this=OBLIQUE_setfi2ndrstnd'
217         """
218         re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
219
220         while True:
221             m = re_var.search(args)
222             if not m:
223                 break
224             lookup = m.group(0)[2:-1]
225             value = var_dict.get(lookup, '')
226             args = args[:m.start(0)] + value + args[m.end(0):]
227         return args
228
229     def GetMakeArguments(self, board):
230         """Returns 'make' arguments for a given board
231
232         The flags are in a section called 'make-flags'. Flags are named
233         after the target they represent, for example snapper9260=TESTING=1
234         will pass TESTING=1 to make when building the snapper9260 board.
235
236         References to other boards can be added in the string also. For
237         example:
238
239         [make-flags]
240         at91-boards=ENABLE_AT91_TEST=1
241         snapper9260=${at91-boards} BUILD_TAG=442
242         snapper9g45=${at91-boards} BUILD_TAG=443
243
244         This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
245         and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
246
247         A special 'target' variable is set to the board target.
248
249         Args:
250             board: Board object for the board to check.
251         Returns:
252             'make' flags for that board, or '' if none
253         """
254         self._make_flags['target'] = board.target
255         arg_str = self.ResolveReferences(self._make_flags,
256                            self._make_flags.get(board.target, ''))
257         args = arg_str.split(' ')
258         i = 0
259         while i < len(args):
260             if not args[i]:
261                 del args[i]
262             else:
263                 i += 1
264         return args