xref: /openbmc/u-boot/tools/buildman/toolchain.py (revision 93e14596)
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            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
88class 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
247