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