xref: /openbmc/u-boot/tools/buildman/toolchain.py (revision 9b83bfdc)
1fc3fe1c2SSimon Glass# Copyright (c) 2012 The Chromium OS Authors.
2fc3fe1c2SSimon Glass#
31a459660SWolfgang Denk# SPDX-License-Identifier:	GPL-2.0+
4fc3fe1c2SSimon Glass#
5fc3fe1c2SSimon Glass
64281ad8eSSimon Glassimport re
7fc3fe1c2SSimon Glassimport glob
8fc3fe1c2SSimon Glassimport os
9fc3fe1c2SSimon Glass
10fc3fe1c2SSimon Glassimport bsettings
11fc3fe1c2SSimon Glassimport command
12fc3fe1c2SSimon Glass
13fc3fe1c2SSimon Glassclass Toolchain:
14fc3fe1c2SSimon Glass    """A single toolchain
15fc3fe1c2SSimon Glass
16fc3fe1c2SSimon Glass    Public members:
17fc3fe1c2SSimon Glass        gcc: Full path to C compiler
18fc3fe1c2SSimon Glass        path: Directory path containing C compiler
19fc3fe1c2SSimon Glass        cross: Cross compile string, e.g. 'arm-linux-'
20fc3fe1c2SSimon Glass        arch: Architecture of toolchain as determined from the first
21fc3fe1c2SSimon Glass                component of the filename. E.g. arm-linux-gcc becomes arm
22fc3fe1c2SSimon Glass    """
23fc3fe1c2SSimon Glass
24fc3fe1c2SSimon Glass    def __init__(self, fname, test, verbose=False):
25fc3fe1c2SSimon Glass        """Create a new toolchain object.
26fc3fe1c2SSimon Glass
27fc3fe1c2SSimon Glass        Args:
28fc3fe1c2SSimon Glass            fname: Filename of the gcc component
29fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
30fc3fe1c2SSimon Glass        """
31fc3fe1c2SSimon Glass        self.gcc = fname
32fc3fe1c2SSimon Glass        self.path = os.path.dirname(fname)
33b5324123SSimon Glass
34b5324123SSimon Glass        # Find the CROSS_COMPILE prefix to use for U-Boot. For example,
35b5324123SSimon Glass        # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'.
36b5324123SSimon Glass        basename = os.path.basename(fname)
37b5324123SSimon Glass        pos = basename.rfind('-')
38b5324123SSimon Glass        self.cross = basename[:pos + 1] if pos != -1 else ''
39b5324123SSimon Glass
40b5324123SSimon Glass        # The architecture is the first part of the name
41fc3fe1c2SSimon Glass        pos = self.cross.find('-')
42fc3fe1c2SSimon Glass        self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
43fc3fe1c2SSimon Glass
44bb1501f2SSimon Glass        env = self.MakeEnvironment(False)
45fc3fe1c2SSimon Glass
46fc3fe1c2SSimon Glass        # As a basic sanity check, run the C compiler with --version
47fc3fe1c2SSimon Glass        cmd = [fname, '--version']
48fc3fe1c2SSimon Glass        if test:
498bb2bddcSStephen Warren            result = command.RunPipe([cmd], capture=True, env=env,
508bb2bddcSStephen Warren                                     raise_on_error=False)
51fc3fe1c2SSimon Glass            self.ok = result.return_code == 0
52fc3fe1c2SSimon Glass            if verbose:
53fc3fe1c2SSimon Glass                print 'Tool chain test: ',
54fc3fe1c2SSimon Glass                if self.ok:
55fc3fe1c2SSimon Glass                    print 'OK'
56fc3fe1c2SSimon Glass                else:
57fc3fe1c2SSimon Glass                    print 'BAD'
58fc3fe1c2SSimon Glass                    print 'Command: ', cmd
59fc3fe1c2SSimon Glass                    print result.stdout
60fc3fe1c2SSimon Glass                    print result.stderr
61fc3fe1c2SSimon Glass        else:
62fc3fe1c2SSimon Glass            self.ok = True
63fc3fe1c2SSimon Glass        self.priority = self.GetPriority(fname)
64fc3fe1c2SSimon Glass
65fc3fe1c2SSimon Glass    def GetPriority(self, fname):
66fc3fe1c2SSimon Glass        """Return the priority of the toolchain.
67fc3fe1c2SSimon Glass
68fc3fe1c2SSimon Glass        Toolchains are ranked according to their suitability by their
69fc3fe1c2SSimon Glass        filename prefix.
70fc3fe1c2SSimon Glass
71fc3fe1c2SSimon Glass        Args:
72fc3fe1c2SSimon Glass            fname: Filename of toolchain
73fc3fe1c2SSimon Glass        Returns:
74fc3fe1c2SSimon Glass            Priority of toolchain, 0=highest, 20=lowest.
75fc3fe1c2SSimon Glass        """
768708267fSMasahiro Yamada        priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
77fc3fe1c2SSimon Glass            '-none-linux-gnueabi', '-uclinux', '-none-eabi',
78fc3fe1c2SSimon Glass            '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
79fc3fe1c2SSimon Glass        for prio in range(len(priority_list)):
80fc3fe1c2SSimon Glass            if priority_list[prio] in fname:
81fc3fe1c2SSimon Glass                return prio
82fc3fe1c2SSimon Glass        return prio
83fc3fe1c2SSimon Glass
84bb1501f2SSimon Glass    def MakeEnvironment(self, full_path):
85fc3fe1c2SSimon Glass        """Returns an environment for using the toolchain.
86fc3fe1c2SSimon Glass
87bb1501f2SSimon Glass        Thie takes the current environment and adds CROSS_COMPILE so that
88bb1501f2SSimon Glass        the tool chain will operate correctly.
89bb1501f2SSimon Glass
90bb1501f2SSimon Glass        Args:
91bb1501f2SSimon Glass            full_path: Return the full path in CROSS_COMPILE and don't set
92bb1501f2SSimon Glass                PATH
93fc3fe1c2SSimon Glass        """
94fc3fe1c2SSimon Glass        env = dict(os.environ)
95bb1501f2SSimon Glass        if full_path:
96bb1501f2SSimon Glass            env['CROSS_COMPILE'] = os.path.join(self.path, self.cross)
97bb1501f2SSimon Glass        else:
98fc3fe1c2SSimon Glass            env['CROSS_COMPILE'] = self.cross
99f210b587SSimon Glass            env['PATH'] = self.path + ':' + env['PATH']
100bb1501f2SSimon Glass
101fc3fe1c2SSimon Glass        return env
102fc3fe1c2SSimon Glass
103fc3fe1c2SSimon Glass
104fc3fe1c2SSimon Glassclass Toolchains:
105fc3fe1c2SSimon Glass    """Manage a list of toolchains for building U-Boot
106fc3fe1c2SSimon Glass
107fc3fe1c2SSimon Glass    We select one toolchain for each architecture type
108fc3fe1c2SSimon Glass
109fc3fe1c2SSimon Glass    Public members:
110fc3fe1c2SSimon Glass        toolchains: Dict of Toolchain objects, keyed by architecture name
111fc3fe1c2SSimon Glass        paths: List of paths to check for toolchains (may contain wildcards)
112fc3fe1c2SSimon Glass    """
113fc3fe1c2SSimon Glass
114fc3fe1c2SSimon Glass    def __init__(self):
115fc3fe1c2SSimon Glass        self.toolchains = {}
116fc3fe1c2SSimon Glass        self.paths = []
117d4144e45SSimon Glass        self._make_flags = dict(bsettings.GetItems('make-flags'))
118d4144e45SSimon Glass
119d4144e45SSimon Glass    def GetSettings(self):
1204281ad8eSSimon Glass        toolchains = bsettings.GetItems('toolchain')
1214281ad8eSSimon Glass        if not toolchains:
1224281ad8eSSimon Glass            print ("Warning: No tool chains - please add a [toolchain] section"
1234281ad8eSSimon Glass                 " to your buildman config file %s. See README for details" %
1241826a18dSMasahiro Yamada                 bsettings.config_fname)
1254281ad8eSSimon Glass
1264281ad8eSSimon Glass        for name, value in toolchains:
127fc3fe1c2SSimon Glass            if '*' in value:
128fc3fe1c2SSimon Glass                self.paths += glob.glob(value)
129fc3fe1c2SSimon Glass            else:
130fc3fe1c2SSimon Glass                self.paths.append(value)
131fc3fe1c2SSimon Glass
132fc3fe1c2SSimon Glass    def Add(self, fname, test=True, verbose=False):
133fc3fe1c2SSimon Glass        """Add a toolchain to our list
134fc3fe1c2SSimon Glass
135fc3fe1c2SSimon Glass        We select the given toolchain as our preferred one for its
136fc3fe1c2SSimon Glass        architecture if it is a higher priority than the others.
137fc3fe1c2SSimon Glass
138fc3fe1c2SSimon Glass        Args:
139fc3fe1c2SSimon Glass            fname: Filename of toolchain's gcc driver
140fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
141fc3fe1c2SSimon Glass        """
142fc3fe1c2SSimon Glass        toolchain = Toolchain(fname, test, verbose)
143fc3fe1c2SSimon Glass        add_it = toolchain.ok
144fc3fe1c2SSimon Glass        if toolchain.arch in self.toolchains:
145fc3fe1c2SSimon Glass            add_it = (toolchain.priority <
146fc3fe1c2SSimon Glass                        self.toolchains[toolchain.arch].priority)
147fc3fe1c2SSimon Glass        if add_it:
148fc3fe1c2SSimon Glass            self.toolchains[toolchain.arch] = toolchain
149fc3fe1c2SSimon Glass
150fc3fe1c2SSimon Glass    def Scan(self, verbose):
151fc3fe1c2SSimon Glass        """Scan for available toolchains and select the best for each arch.
152fc3fe1c2SSimon Glass
153fc3fe1c2SSimon Glass        We look for all the toolchains we can file, figure out the
154fc3fe1c2SSimon Glass        architecture for each, and whether it works. Then we select the
155fc3fe1c2SSimon Glass        highest priority toolchain for each arch.
156fc3fe1c2SSimon Glass
157fc3fe1c2SSimon Glass        Args:
158fc3fe1c2SSimon Glass            verbose: True to print out progress information
159fc3fe1c2SSimon Glass        """
160fc3fe1c2SSimon Glass        if verbose: print 'Scanning for tool chains'
161fc3fe1c2SSimon Glass        for path in self.paths:
162fc3fe1c2SSimon Glass            if verbose: print "   - scanning path '%s'" % path
163fc3fe1c2SSimon Glass            for subdir in ['.', 'bin', 'usr/bin']:
164fc3fe1c2SSimon Glass                dirname = os.path.join(path, subdir)
165fc3fe1c2SSimon Glass                if verbose: print "      - looking in '%s'" % dirname
166fc3fe1c2SSimon Glass                for fname in glob.glob(dirname + '/*gcc'):
167fc3fe1c2SSimon Glass                    if verbose: print "         - found '%s'" % fname
168fc3fe1c2SSimon Glass                    self.Add(fname, True, verbose)
169fc3fe1c2SSimon Glass
170fc3fe1c2SSimon Glass    def List(self):
171fc3fe1c2SSimon Glass        """List out the selected toolchains for each architecture"""
172fc3fe1c2SSimon Glass        print 'List of available toolchains (%d):' % len(self.toolchains)
173fc3fe1c2SSimon Glass        if len(self.toolchains):
174fc3fe1c2SSimon Glass            for key, value in sorted(self.toolchains.iteritems()):
175fc3fe1c2SSimon Glass                print '%-10s: %s' % (key, value.gcc)
176fc3fe1c2SSimon Glass        else:
177fc3fe1c2SSimon Glass            print 'None'
178fc3fe1c2SSimon Glass
179fc3fe1c2SSimon Glass    def Select(self, arch):
180fc3fe1c2SSimon Glass        """Returns the toolchain for a given architecture
181fc3fe1c2SSimon Glass
182fc3fe1c2SSimon Glass        Args:
183fc3fe1c2SSimon Glass            args: Name of architecture (e.g. 'arm', 'ppc_8xx')
184fc3fe1c2SSimon Glass
185fc3fe1c2SSimon Glass        returns:
186fc3fe1c2SSimon Glass            toolchain object, or None if none found
187fc3fe1c2SSimon Glass        """
188*9b83bfdcSSimon Glass        for tag, value in bsettings.GetItems('toolchain-alias'):
189*9b83bfdcSSimon Glass            if arch == tag:
190*9b83bfdcSSimon Glass                for alias in value.split():
191*9b83bfdcSSimon Glass                    if alias in self.toolchains:
192*9b83bfdcSSimon Glass                        return self.toolchains[alias]
193fc3fe1c2SSimon Glass
194fc3fe1c2SSimon Glass        if not arch in self.toolchains:
195fc3fe1c2SSimon Glass            raise ValueError, ("No tool chain found for arch '%s'" % arch)
196fc3fe1c2SSimon Glass        return self.toolchains[arch]
1974281ad8eSSimon Glass
1984281ad8eSSimon Glass    def ResolveReferences(self, var_dict, args):
1994281ad8eSSimon Glass        """Resolve variable references in a string
2004281ad8eSSimon Glass
2014281ad8eSSimon Glass        This converts ${blah} within the string to the value of blah.
2024281ad8eSSimon Glass        This function works recursively.
2034281ad8eSSimon Glass
2044281ad8eSSimon Glass        Args:
2054281ad8eSSimon Glass            var_dict: Dictionary containing variables and their values
2064281ad8eSSimon Glass            args: String containing make arguments
2074281ad8eSSimon Glass        Returns:
2084281ad8eSSimon Glass            Resolved string
2094281ad8eSSimon Glass
2104281ad8eSSimon Glass        >>> bsettings.Setup()
2114281ad8eSSimon Glass        >>> tcs = Toolchains()
2124281ad8eSSimon Glass        >>> tcs.Add('fred', False)
2134281ad8eSSimon Glass        >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
2144281ad8eSSimon Glass                        'second' : '2nd'}
2154281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
2164281ad8eSSimon Glass        'this=OBLIQUE_set'
2174281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
2184281ad8eSSimon Glass        'this=OBLIQUE_setfi2ndrstnd'
2194281ad8eSSimon Glass        """
220f60c9d4fSSimon Glass        re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
2214281ad8eSSimon Glass
2224281ad8eSSimon Glass        while True:
2234281ad8eSSimon Glass            m = re_var.search(args)
2244281ad8eSSimon Glass            if not m:
2254281ad8eSSimon Glass                break
2264281ad8eSSimon Glass            lookup = m.group(0)[2:-1]
2274281ad8eSSimon Glass            value = var_dict.get(lookup, '')
2284281ad8eSSimon Glass            args = args[:m.start(0)] + value + args[m.end(0):]
2294281ad8eSSimon Glass        return args
2304281ad8eSSimon Glass
2314281ad8eSSimon Glass    def GetMakeArguments(self, board):
2324281ad8eSSimon Glass        """Returns 'make' arguments for a given board
2334281ad8eSSimon Glass
2344281ad8eSSimon Glass        The flags are in a section called 'make-flags'. Flags are named
2354281ad8eSSimon Glass        after the target they represent, for example snapper9260=TESTING=1
2364281ad8eSSimon Glass        will pass TESTING=1 to make when building the snapper9260 board.
2374281ad8eSSimon Glass
2384281ad8eSSimon Glass        References to other boards can be added in the string also. For
2394281ad8eSSimon Glass        example:
2404281ad8eSSimon Glass
2414281ad8eSSimon Glass        [make-flags]
2424281ad8eSSimon Glass        at91-boards=ENABLE_AT91_TEST=1
2434281ad8eSSimon Glass        snapper9260=${at91-boards} BUILD_TAG=442
2444281ad8eSSimon Glass        snapper9g45=${at91-boards} BUILD_TAG=443
2454281ad8eSSimon Glass
2464281ad8eSSimon Glass        This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
2474281ad8eSSimon Glass        and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
2484281ad8eSSimon Glass
2494281ad8eSSimon Glass        A special 'target' variable is set to the board target.
2504281ad8eSSimon Glass
2514281ad8eSSimon Glass        Args:
2524281ad8eSSimon Glass            board: Board object for the board to check.
2534281ad8eSSimon Glass        Returns:
2544281ad8eSSimon Glass            'make' flags for that board, or '' if none
2554281ad8eSSimon Glass        """
2564281ad8eSSimon Glass        self._make_flags['target'] = board.target
2574281ad8eSSimon Glass        arg_str = self.ResolveReferences(self._make_flags,
2584281ad8eSSimon Glass                           self._make_flags.get(board.target, ''))
2594281ad8eSSimon Glass        args = arg_str.split(' ')
2604281ad8eSSimon Glass        i = 0
2614281ad8eSSimon Glass        while i < len(args):
2624281ad8eSSimon Glass            if not args[i]:
2634281ad8eSSimon Glass                del args[i]
2644281ad8eSSimon Glass            else:
2654281ad8eSSimon Glass                i += 1
2664281ad8eSSimon Glass        return args
267