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