xref: /openbmc/u-boot/tools/buildman/toolchain.py (revision b5493d17)
1# Copyright (c) 2012 The Chromium OS Authors.
2#
3# SPDX-License-Identifier:	GPL-2.0+
4#
5
6import re
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)
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                                     raise_on_error=False)
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        """
69        priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
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
89class 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 = []
102        self._make_flags = dict(bsettings.GetItems('make-flags'))
103
104    def GetSettings(self):
105        toolchains = bsettings.GetItems('toolchain')
106        if not toolchains:
107            print ("Warning: No tool chains - please add a [toolchain] section"
108                 " to your buildman config file %s. See README for details" %
109                 bsettings.config_fname)
110
111        for name, value in toolchains:
112            if '*' in value:
113                self.paths += glob.glob(value)
114            else:
115                self.paths.append(value)
116
117    def Add(self, fname, test=True, verbose=False):
118        """Add a toolchain to our list
119
120        We select the given toolchain as our preferred one for its
121        architecture if it is a higher priority than the others.
122
123        Args:
124            fname: Filename of toolchain's gcc driver
125            test: True to run the toolchain to test it
126        """
127        toolchain = Toolchain(fname, test, verbose)
128        add_it = toolchain.ok
129        if toolchain.arch in self.toolchains:
130            add_it = (toolchain.priority <
131                        self.toolchains[toolchain.arch].priority)
132        if add_it:
133            self.toolchains[toolchain.arch] = toolchain
134
135    def Scan(self, verbose):
136        """Scan for available toolchains and select the best for each arch.
137
138        We look for all the toolchains we can file, figure out the
139        architecture for each, and whether it works. Then we select the
140        highest priority toolchain for each arch.
141
142        Args:
143            verbose: True to print out progress information
144        """
145        if verbose: print 'Scanning for tool chains'
146        for path in self.paths:
147            if verbose: print "   - scanning path '%s'" % path
148            for subdir in ['.', 'bin', 'usr/bin']:
149                dirname = os.path.join(path, subdir)
150                if verbose: print "      - looking in '%s'" % dirname
151                for fname in glob.glob(dirname + '/*gcc'):
152                    if verbose: print "         - found '%s'" % fname
153                    self.Add(fname, True, verbose)
154
155    def List(self):
156        """List out the selected toolchains for each architecture"""
157        print 'List of available toolchains (%d):' % len(self.toolchains)
158        if len(self.toolchains):
159            for key, value in sorted(self.toolchains.iteritems()):
160                print '%-10s: %s' % (key, value.gcc)
161        else:
162            print 'None'
163
164    def Select(self, arch):
165        """Returns the toolchain for a given architecture
166
167        Args:
168            args: Name of architecture (e.g. 'arm', 'ppc_8xx')
169
170        returns:
171            toolchain object, or None if none found
172        """
173        for name, value in bsettings.GetItems('toolchain-alias'):
174            if arch == name:
175                arch = value
176
177        if not arch in self.toolchains:
178            raise ValueError, ("No tool chain found for arch '%s'" % arch)
179        return self.toolchains[arch]
180
181    def ResolveReferences(self, var_dict, args):
182        """Resolve variable references in a string
183
184        This converts ${blah} within the string to the value of blah.
185        This function works recursively.
186
187        Args:
188            var_dict: Dictionary containing variables and their values
189            args: String containing make arguments
190        Returns:
191            Resolved string
192
193        >>> bsettings.Setup()
194        >>> tcs = Toolchains()
195        >>> tcs.Add('fred', False)
196        >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
197                        'second' : '2nd'}
198        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
199        'this=OBLIQUE_set'
200        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
201        'this=OBLIQUE_setfi2ndrstnd'
202        """
203        re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
204
205        while True:
206            m = re_var.search(args)
207            if not m:
208                break
209            lookup = m.group(0)[2:-1]
210            value = var_dict.get(lookup, '')
211            args = args[:m.start(0)] + value + args[m.end(0):]
212        return args
213
214    def GetMakeArguments(self, board):
215        """Returns 'make' arguments for a given board
216
217        The flags are in a section called 'make-flags'. Flags are named
218        after the target they represent, for example snapper9260=TESTING=1
219        will pass TESTING=1 to make when building the snapper9260 board.
220
221        References to other boards can be added in the string also. For
222        example:
223
224        [make-flags]
225        at91-boards=ENABLE_AT91_TEST=1
226        snapper9260=${at91-boards} BUILD_TAG=442
227        snapper9g45=${at91-boards} BUILD_TAG=443
228
229        This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
230        and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
231
232        A special 'target' variable is set to the board target.
233
234        Args:
235            board: Board object for the board to check.
236        Returns:
237            'make' flags for that board, or '' if none
238        """
239        self._make_flags['target'] = board.target
240        arg_str = self.ResolveReferences(self._make_flags,
241                           self._make_flags.get(board.target, ''))
242        args = arg_str.split(' ')
243        i = 0
244        while i < len(args):
245            if not args[i]:
246                del args[i]
247            else:
248                i += 1
249        return args
250