xref: /openbmc/u-boot/tools/buildman/toolchain.py (revision ea818dbb)
1# Copyright (c) 2012 The Chromium OS Authors.
2#
3# See file CREDITS for list of people who contributed to this
4# project.
5#
6# This program is free software; you can redistribute it and/or
7# modify it under the terms of the GNU General Public License as
8# published by the Free Software Foundation; either version 2 of
9# the License, or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19# MA 02111-1307 USA
20#
21
22import glob
23import os
24
25import bsettings
26import command
27
28class Toolchain:
29    """A single toolchain
30
31    Public members:
32        gcc: Full path to C compiler
33        path: Directory path containing C compiler
34        cross: Cross compile string, e.g. 'arm-linux-'
35        arch: Architecture of toolchain as determined from the first
36                component of the filename. E.g. arm-linux-gcc becomes arm
37    """
38
39    def __init__(self, fname, test, verbose=False):
40        """Create a new toolchain object.
41
42        Args:
43            fname: Filename of the gcc component
44            test: True to run the toolchain to test it
45        """
46        self.gcc = fname
47        self.path = os.path.dirname(fname)
48        self.cross = os.path.basename(fname)[:-3]
49        pos = self.cross.find('-')
50        self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
51
52        env = self.MakeEnvironment()
53
54        # As a basic sanity check, run the C compiler with --version
55        cmd = [fname, '--version']
56        if test:
57            result = command.RunPipe([cmd], capture=True, env=env)
58            self.ok = result.return_code == 0
59            if verbose:
60                print 'Tool chain test: ',
61                if self.ok:
62                    print 'OK'
63                else:
64                    print 'BAD'
65                    print 'Command: ', cmd
66                    print result.stdout
67                    print result.stderr
68        else:
69            self.ok = True
70        self.priority = self.GetPriority(fname)
71
72    def GetPriority(self, fname):
73        """Return the priority of the toolchain.
74
75        Toolchains are ranked according to their suitability by their
76        filename prefix.
77
78        Args:
79            fname: Filename of toolchain
80        Returns:
81            Priority of toolchain, 0=highest, 20=lowest.
82        """
83        priority_list = ['-elf', '-unknown-linux-gnu', '-linux', '-elf',
84            '-none-linux-gnueabi', '-uclinux', '-none-eabi',
85            '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
86        for prio in range(len(priority_list)):
87            if priority_list[prio] in fname:
88                return prio
89        return prio
90
91    def MakeEnvironment(self):
92        """Returns an environment for using the toolchain.
93
94        Thie takes the current environment, adds CROSS_COMPILE and
95        augments PATH so that the toolchain will operate correctly.
96        """
97        env = dict(os.environ)
98        env['CROSS_COMPILE'] = self.cross
99        env['PATH'] += (':' + self.path)
100        return env
101
102
103class Toolchains:
104    """Manage a list of toolchains for building U-Boot
105
106    We select one toolchain for each architecture type
107
108    Public members:
109        toolchains: Dict of Toolchain objects, keyed by architecture name
110        paths: List of paths to check for toolchains (may contain wildcards)
111    """
112
113    def __init__(self):
114        self.toolchains = {}
115        self.paths = []
116        for name, value in bsettings.GetItems('toolchain'):
117            if '*' in value:
118                self.paths += glob.glob(value)
119            else:
120                self.paths.append(value)
121
122
123    def Add(self, fname, test=True, verbose=False):
124        """Add a toolchain to our list
125
126        We select the given toolchain as our preferred one for its
127        architecture if it is a higher priority than the others.
128
129        Args:
130            fname: Filename of toolchain's gcc driver
131            test: True to run the toolchain to test it
132        """
133        toolchain = Toolchain(fname, test, verbose)
134        add_it = toolchain.ok
135        if toolchain.arch in self.toolchains:
136            add_it = (toolchain.priority <
137                        self.toolchains[toolchain.arch].priority)
138        if add_it:
139            self.toolchains[toolchain.arch] = toolchain
140
141    def Scan(self, verbose):
142        """Scan for available toolchains and select the best for each arch.
143
144        We look for all the toolchains we can file, figure out the
145        architecture for each, and whether it works. Then we select the
146        highest priority toolchain for each arch.
147
148        Args:
149            verbose: True to print out progress information
150        """
151        if verbose: print 'Scanning for tool chains'
152        for path in self.paths:
153            if verbose: print "   - scanning path '%s'" % path
154            for subdir in ['.', 'bin', 'usr/bin']:
155                dirname = os.path.join(path, subdir)
156                if verbose: print "      - looking in '%s'" % dirname
157                for fname in glob.glob(dirname + '/*gcc'):
158                    if verbose: print "         - found '%s'" % fname
159                    self.Add(fname, True, verbose)
160
161    def List(self):
162        """List out the selected toolchains for each architecture"""
163        print 'List of available toolchains (%d):' % len(self.toolchains)
164        if len(self.toolchains):
165            for key, value in sorted(self.toolchains.iteritems()):
166                print '%-10s: %s' % (key, value.gcc)
167        else:
168            print 'None'
169
170    def Select(self, arch):
171        """Returns the toolchain for a given architecture
172
173        Args:
174            args: Name of architecture (e.g. 'arm', 'ppc_8xx')
175
176        returns:
177            toolchain object, or None if none found
178        """
179        for name, value in bsettings.GetItems('toolchain-alias'):
180            if arch == name:
181                arch = value
182
183        if not arch in self.toolchains:
184            raise ValueError, ("No tool chain found for arch '%s'" % arch)
185        return self.toolchains[arch]
186