]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - tools/buildman/toolchain.py
buildman: Allow make flags to be specified for each board
[karo-tx-uboot.git] / 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         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:
42             result = command.RunPipe([cmd], capture=True, env=env)
43             self.ok = result.return_code == 0
44             if verbose:
45                 print 'Tool chain test: ',
46                 if self.ok:
47                     print 'OK'
48                 else:
49                     print 'BAD'
50                     print 'Command: ', cmd
51                     print result.stdout
52                     print result.stderr
53         else:
54             self.ok = True
55         self.priority = self.GetPriority(fname)
56
57     def GetPriority(self, fname):
58         """Return the priority of the toolchain.
59
60         Toolchains are ranked according to their suitability by their
61         filename prefix.
62
63         Args:
64             fname: Filename of toolchain
65         Returns:
66             Priority of toolchain, 0=highest, 20=lowest.
67         """
68         priority_list = ['-elf', '-unknown-linux-gnu', '-linux', '-elf',
69             '-none-linux-gnueabi', '-uclinux', '-none-eabi',
70             '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
71         for prio in range(len(priority_list)):
72             if priority_list[prio] in fname:
73                 return prio
74         return prio
75
76     def MakeEnvironment(self):
77         """Returns an environment for using the toolchain.
78
79         Thie takes the current environment, adds CROSS_COMPILE and
80         augments PATH so that the toolchain will operate correctly.
81         """
82         env = dict(os.environ)
83         env['CROSS_COMPILE'] = self.cross
84         env['PATH'] += (':' + self.path)
85         return env
86
87
88 class Toolchains:
89     """Manage a list of toolchains for building U-Boot
90
91     We select one toolchain for each architecture type
92
93     Public members:
94         toolchains: Dict of Toolchain objects, keyed by architecture name
95         paths: List of paths to check for toolchains (may contain wildcards)
96     """
97
98     def __init__(self):
99         self.toolchains = {}
100         self.paths = []
101         toolchains = bsettings.GetItems('toolchain')
102         if not toolchains:
103             print ("Warning: No tool chains - please add a [toolchain] section"
104                  " to your buildman config file %s. See README for details" %
105                  config_fname)
106
107         for name, value in toolchains:
108             if '*' in value:
109                 self.paths += glob.glob(value)
110             else:
111                 self.paths.append(value)
112         self._make_flags = dict(bsettings.GetItems('make-flags'))
113
114     def Add(self, fname, test=True, verbose=False):
115         """Add a toolchain to our list
116
117         We select the given toolchain as our preferred one for its
118         architecture if it is a higher priority than the others.
119
120         Args:
121             fname: Filename of toolchain's gcc driver
122             test: True to run the toolchain to test it
123         """
124         toolchain = Toolchain(fname, test, verbose)
125         add_it = toolchain.ok
126         if toolchain.arch in self.toolchains:
127             add_it = (toolchain.priority <
128                         self.toolchains[toolchain.arch].priority)
129         if add_it:
130             self.toolchains[toolchain.arch] = toolchain
131
132     def Scan(self, verbose):
133         """Scan for available toolchains and select the best for each arch.
134
135         We look for all the toolchains we can file, figure out the
136         architecture for each, and whether it works. Then we select the
137         highest priority toolchain for each arch.
138
139         Args:
140             verbose: True to print out progress information
141         """
142         if verbose: print 'Scanning for tool chains'
143         for path in self.paths:
144             if verbose: print "   - scanning path '%s'" % path
145             for subdir in ['.', 'bin', 'usr/bin']:
146                 dirname = os.path.join(path, subdir)
147                 if verbose: print "      - looking in '%s'" % dirname
148                 for fname in glob.glob(dirname + '/*gcc'):
149                     if verbose: print "         - found '%s'" % fname
150                     self.Add(fname, True, verbose)
151
152     def List(self):
153         """List out the selected toolchains for each architecture"""
154         print 'List of available toolchains (%d):' % len(self.toolchains)
155         if len(self.toolchains):
156             for key, value in sorted(self.toolchains.iteritems()):
157                 print '%-10s: %s' % (key, value.gcc)
158         else:
159             print 'None'
160
161     def Select(self, arch):
162         """Returns the toolchain for a given architecture
163
164         Args:
165             args: Name of architecture (e.g. 'arm', 'ppc_8xx')
166
167         returns:
168             toolchain object, or None if none found
169         """
170         for name, value in bsettings.GetItems('toolchain-alias'):
171             if arch == name:
172                 arch = value
173
174         if not arch in self.toolchains:
175             raise ValueError, ("No tool chain found for arch '%s'" % arch)
176         return self.toolchains[arch]
177
178     def ResolveReferences(self, var_dict, args):
179         """Resolve variable references in a string
180
181         This converts ${blah} within the string to the value of blah.
182         This function works recursively.
183
184         Args:
185             var_dict: Dictionary containing variables and their values
186             args: String containing make arguments
187         Returns:
188             Resolved string
189
190         >>> bsettings.Setup()
191         >>> tcs = Toolchains()
192         >>> tcs.Add('fred', False)
193         >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
194                         'second' : '2nd'}
195         >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
196         'this=OBLIQUE_set'
197         >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
198         'this=OBLIQUE_setfi2ndrstnd'
199         """
200         re_var = re.compile('(\$\{[a-z0-9A-Z]{1,}\})')
201
202         while True:
203             m = re_var.search(args)
204             if not m:
205                 break
206             lookup = m.group(0)[2:-1]
207             value = var_dict.get(lookup, '')
208             args = args[:m.start(0)] + value + args[m.end(0):]
209         return args
210
211     def GetMakeArguments(self, board):
212         """Returns 'make' arguments for a given board
213
214         The flags are in a section called 'make-flags'. Flags are named
215         after the target they represent, for example snapper9260=TESTING=1
216         will pass TESTING=1 to make when building the snapper9260 board.
217
218         References to other boards can be added in the string also. For
219         example:
220
221         [make-flags]
222         at91-boards=ENABLE_AT91_TEST=1
223         snapper9260=${at91-boards} BUILD_TAG=442
224         snapper9g45=${at91-boards} BUILD_TAG=443
225
226         This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
227         and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
228
229         A special 'target' variable is set to the board target.
230
231         Args:
232             board: Board object for the board to check.
233         Returns:
234             'make' flags for that board, or '' if none
235         """
236         self._make_flags['target'] = board.target
237         arg_str = self.ResolveReferences(self._make_flags,
238                            self._make_flags.get(board.target, ''))
239         args = arg_str.split(' ')
240         i = 0
241         while i < len(args):
242             if not args[i]:
243                 del args[i]
244             else:
245                 i += 1
246         return args