xref: /rk3399_rockchip-uboot/tools/buildman/toolchain.py (revision 4281ad8e7fcb304724127281f258d198001fd41a)
1fc3fe1c2SSimon Glass# Copyright (c) 2012 The Chromium OS Authors.
2fc3fe1c2SSimon Glass#
31a459660SWolfgang Denk# SPDX-License-Identifier:	GPL-2.0+
4fc3fe1c2SSimon Glass#
5fc3fe1c2SSimon Glass
6*4281ad8eSSimon 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)
33fc3fe1c2SSimon Glass        self.cross = os.path.basename(fname)[:-3]
34fc3fe1c2SSimon Glass        pos = self.cross.find('-')
35fc3fe1c2SSimon Glass        self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
36fc3fe1c2SSimon Glass
37fc3fe1c2SSimon Glass        env = self.MakeEnvironment()
38fc3fe1c2SSimon Glass
39fc3fe1c2SSimon Glass        # As a basic sanity check, run the C compiler with --version
40fc3fe1c2SSimon Glass        cmd = [fname, '--version']
41fc3fe1c2SSimon Glass        if test:
42fc3fe1c2SSimon Glass            result = command.RunPipe([cmd], capture=True, env=env)
43fc3fe1c2SSimon Glass            self.ok = result.return_code == 0
44fc3fe1c2SSimon Glass            if verbose:
45fc3fe1c2SSimon Glass                print 'Tool chain test: ',
46fc3fe1c2SSimon Glass                if self.ok:
47fc3fe1c2SSimon Glass                    print 'OK'
48fc3fe1c2SSimon Glass                else:
49fc3fe1c2SSimon Glass                    print 'BAD'
50fc3fe1c2SSimon Glass                    print 'Command: ', cmd
51fc3fe1c2SSimon Glass                    print result.stdout
52fc3fe1c2SSimon Glass                    print result.stderr
53fc3fe1c2SSimon Glass        else:
54fc3fe1c2SSimon Glass            self.ok = True
55fc3fe1c2SSimon Glass        self.priority = self.GetPriority(fname)
56fc3fe1c2SSimon Glass
57fc3fe1c2SSimon Glass    def GetPriority(self, fname):
58fc3fe1c2SSimon Glass        """Return the priority of the toolchain.
59fc3fe1c2SSimon Glass
60fc3fe1c2SSimon Glass        Toolchains are ranked according to their suitability by their
61fc3fe1c2SSimon Glass        filename prefix.
62fc3fe1c2SSimon Glass
63fc3fe1c2SSimon Glass        Args:
64fc3fe1c2SSimon Glass            fname: Filename of toolchain
65fc3fe1c2SSimon Glass        Returns:
66fc3fe1c2SSimon Glass            Priority of toolchain, 0=highest, 20=lowest.
67fc3fe1c2SSimon Glass        """
68fc3fe1c2SSimon Glass        priority_list = ['-elf', '-unknown-linux-gnu', '-linux', '-elf',
69fc3fe1c2SSimon Glass            '-none-linux-gnueabi', '-uclinux', '-none-eabi',
70fc3fe1c2SSimon Glass            '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
71fc3fe1c2SSimon Glass        for prio in range(len(priority_list)):
72fc3fe1c2SSimon Glass            if priority_list[prio] in fname:
73fc3fe1c2SSimon Glass                return prio
74fc3fe1c2SSimon Glass        return prio
75fc3fe1c2SSimon Glass
76fc3fe1c2SSimon Glass    def MakeEnvironment(self):
77fc3fe1c2SSimon Glass        """Returns an environment for using the toolchain.
78fc3fe1c2SSimon Glass
79fc3fe1c2SSimon Glass        Thie takes the current environment, adds CROSS_COMPILE and
80fc3fe1c2SSimon Glass        augments PATH so that the toolchain will operate correctly.
81fc3fe1c2SSimon Glass        """
82fc3fe1c2SSimon Glass        env = dict(os.environ)
83fc3fe1c2SSimon Glass        env['CROSS_COMPILE'] = self.cross
84fc3fe1c2SSimon Glass        env['PATH'] += (':' + self.path)
85fc3fe1c2SSimon Glass        return env
86fc3fe1c2SSimon Glass
87fc3fe1c2SSimon Glass
88fc3fe1c2SSimon Glassclass Toolchains:
89fc3fe1c2SSimon Glass    """Manage a list of toolchains for building U-Boot
90fc3fe1c2SSimon Glass
91fc3fe1c2SSimon Glass    We select one toolchain for each architecture type
92fc3fe1c2SSimon Glass
93fc3fe1c2SSimon Glass    Public members:
94fc3fe1c2SSimon Glass        toolchains: Dict of Toolchain objects, keyed by architecture name
95fc3fe1c2SSimon Glass        paths: List of paths to check for toolchains (may contain wildcards)
96fc3fe1c2SSimon Glass    """
97fc3fe1c2SSimon Glass
98fc3fe1c2SSimon Glass    def __init__(self):
99fc3fe1c2SSimon Glass        self.toolchains = {}
100fc3fe1c2SSimon Glass        self.paths = []
101*4281ad8eSSimon Glass        toolchains = bsettings.GetItems('toolchain')
102*4281ad8eSSimon Glass        if not toolchains:
103*4281ad8eSSimon Glass            print ("Warning: No tool chains - please add a [toolchain] section"
104*4281ad8eSSimon Glass                 " to your buildman config file %s. See README for details" %
105*4281ad8eSSimon Glass                 config_fname)
106*4281ad8eSSimon Glass
107*4281ad8eSSimon Glass        for name, value in toolchains:
108fc3fe1c2SSimon Glass            if '*' in value:
109fc3fe1c2SSimon Glass                self.paths += glob.glob(value)
110fc3fe1c2SSimon Glass            else:
111fc3fe1c2SSimon Glass                self.paths.append(value)
112*4281ad8eSSimon Glass        self._make_flags = dict(bsettings.GetItems('make-flags'))
113fc3fe1c2SSimon Glass
114fc3fe1c2SSimon Glass    def Add(self, fname, test=True, verbose=False):
115fc3fe1c2SSimon Glass        """Add a toolchain to our list
116fc3fe1c2SSimon Glass
117fc3fe1c2SSimon Glass        We select the given toolchain as our preferred one for its
118fc3fe1c2SSimon Glass        architecture if it is a higher priority than the others.
119fc3fe1c2SSimon Glass
120fc3fe1c2SSimon Glass        Args:
121fc3fe1c2SSimon Glass            fname: Filename of toolchain's gcc driver
122fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
123fc3fe1c2SSimon Glass        """
124fc3fe1c2SSimon Glass        toolchain = Toolchain(fname, test, verbose)
125fc3fe1c2SSimon Glass        add_it = toolchain.ok
126fc3fe1c2SSimon Glass        if toolchain.arch in self.toolchains:
127fc3fe1c2SSimon Glass            add_it = (toolchain.priority <
128fc3fe1c2SSimon Glass                        self.toolchains[toolchain.arch].priority)
129fc3fe1c2SSimon Glass        if add_it:
130fc3fe1c2SSimon Glass            self.toolchains[toolchain.arch] = toolchain
131fc3fe1c2SSimon Glass
132fc3fe1c2SSimon Glass    def Scan(self, verbose):
133fc3fe1c2SSimon Glass        """Scan for available toolchains and select the best for each arch.
134fc3fe1c2SSimon Glass
135fc3fe1c2SSimon Glass        We look for all the toolchains we can file, figure out the
136fc3fe1c2SSimon Glass        architecture for each, and whether it works. Then we select the
137fc3fe1c2SSimon Glass        highest priority toolchain for each arch.
138fc3fe1c2SSimon Glass
139fc3fe1c2SSimon Glass        Args:
140fc3fe1c2SSimon Glass            verbose: True to print out progress information
141fc3fe1c2SSimon Glass        """
142fc3fe1c2SSimon Glass        if verbose: print 'Scanning for tool chains'
143fc3fe1c2SSimon Glass        for path in self.paths:
144fc3fe1c2SSimon Glass            if verbose: print "   - scanning path '%s'" % path
145fc3fe1c2SSimon Glass            for subdir in ['.', 'bin', 'usr/bin']:
146fc3fe1c2SSimon Glass                dirname = os.path.join(path, subdir)
147fc3fe1c2SSimon Glass                if verbose: print "      - looking in '%s'" % dirname
148fc3fe1c2SSimon Glass                for fname in glob.glob(dirname + '/*gcc'):
149fc3fe1c2SSimon Glass                    if verbose: print "         - found '%s'" % fname
150fc3fe1c2SSimon Glass                    self.Add(fname, True, verbose)
151fc3fe1c2SSimon Glass
152fc3fe1c2SSimon Glass    def List(self):
153fc3fe1c2SSimon Glass        """List out the selected toolchains for each architecture"""
154fc3fe1c2SSimon Glass        print 'List of available toolchains (%d):' % len(self.toolchains)
155fc3fe1c2SSimon Glass        if len(self.toolchains):
156fc3fe1c2SSimon Glass            for key, value in sorted(self.toolchains.iteritems()):
157fc3fe1c2SSimon Glass                print '%-10s: %s' % (key, value.gcc)
158fc3fe1c2SSimon Glass        else:
159fc3fe1c2SSimon Glass            print 'None'
160fc3fe1c2SSimon Glass
161fc3fe1c2SSimon Glass    def Select(self, arch):
162fc3fe1c2SSimon Glass        """Returns the toolchain for a given architecture
163fc3fe1c2SSimon Glass
164fc3fe1c2SSimon Glass        Args:
165fc3fe1c2SSimon Glass            args: Name of architecture (e.g. 'arm', 'ppc_8xx')
166fc3fe1c2SSimon Glass
167fc3fe1c2SSimon Glass        returns:
168fc3fe1c2SSimon Glass            toolchain object, or None if none found
169fc3fe1c2SSimon Glass        """
170fc3fe1c2SSimon Glass        for name, value in bsettings.GetItems('toolchain-alias'):
171fc3fe1c2SSimon Glass            if arch == name:
172fc3fe1c2SSimon Glass                arch = value
173fc3fe1c2SSimon Glass
174fc3fe1c2SSimon Glass        if not arch in self.toolchains:
175fc3fe1c2SSimon Glass            raise ValueError, ("No tool chain found for arch '%s'" % arch)
176fc3fe1c2SSimon Glass        return self.toolchains[arch]
177*4281ad8eSSimon Glass
178*4281ad8eSSimon Glass    def ResolveReferences(self, var_dict, args):
179*4281ad8eSSimon Glass        """Resolve variable references in a string
180*4281ad8eSSimon Glass
181*4281ad8eSSimon Glass        This converts ${blah} within the string to the value of blah.
182*4281ad8eSSimon Glass        This function works recursively.
183*4281ad8eSSimon Glass
184*4281ad8eSSimon Glass        Args:
185*4281ad8eSSimon Glass            var_dict: Dictionary containing variables and their values
186*4281ad8eSSimon Glass            args: String containing make arguments
187*4281ad8eSSimon Glass        Returns:
188*4281ad8eSSimon Glass            Resolved string
189*4281ad8eSSimon Glass
190*4281ad8eSSimon Glass        >>> bsettings.Setup()
191*4281ad8eSSimon Glass        >>> tcs = Toolchains()
192*4281ad8eSSimon Glass        >>> tcs.Add('fred', False)
193*4281ad8eSSimon Glass        >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
194*4281ad8eSSimon Glass                        'second' : '2nd'}
195*4281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
196*4281ad8eSSimon Glass        'this=OBLIQUE_set'
197*4281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
198*4281ad8eSSimon Glass        'this=OBLIQUE_setfi2ndrstnd'
199*4281ad8eSSimon Glass        """
200*4281ad8eSSimon Glass        re_var = re.compile('(\$\{[a-z0-9A-Z]{1,}\})')
201*4281ad8eSSimon Glass
202*4281ad8eSSimon Glass        while True:
203*4281ad8eSSimon Glass            m = re_var.search(args)
204*4281ad8eSSimon Glass            if not m:
205*4281ad8eSSimon Glass                break
206*4281ad8eSSimon Glass            lookup = m.group(0)[2:-1]
207*4281ad8eSSimon Glass            value = var_dict.get(lookup, '')
208*4281ad8eSSimon Glass            args = args[:m.start(0)] + value + args[m.end(0):]
209*4281ad8eSSimon Glass        return args
210*4281ad8eSSimon Glass
211*4281ad8eSSimon Glass    def GetMakeArguments(self, board):
212*4281ad8eSSimon Glass        """Returns 'make' arguments for a given board
213*4281ad8eSSimon Glass
214*4281ad8eSSimon Glass        The flags are in a section called 'make-flags'. Flags are named
215*4281ad8eSSimon Glass        after the target they represent, for example snapper9260=TESTING=1
216*4281ad8eSSimon Glass        will pass TESTING=1 to make when building the snapper9260 board.
217*4281ad8eSSimon Glass
218*4281ad8eSSimon Glass        References to other boards can be added in the string also. For
219*4281ad8eSSimon Glass        example:
220*4281ad8eSSimon Glass
221*4281ad8eSSimon Glass        [make-flags]
222*4281ad8eSSimon Glass        at91-boards=ENABLE_AT91_TEST=1
223*4281ad8eSSimon Glass        snapper9260=${at91-boards} BUILD_TAG=442
224*4281ad8eSSimon Glass        snapper9g45=${at91-boards} BUILD_TAG=443
225*4281ad8eSSimon Glass
226*4281ad8eSSimon Glass        This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
227*4281ad8eSSimon Glass        and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
228*4281ad8eSSimon Glass
229*4281ad8eSSimon Glass        A special 'target' variable is set to the board target.
230*4281ad8eSSimon Glass
231*4281ad8eSSimon Glass        Args:
232*4281ad8eSSimon Glass            board: Board object for the board to check.
233*4281ad8eSSimon Glass        Returns:
234*4281ad8eSSimon Glass            'make' flags for that board, or '' if none
235*4281ad8eSSimon Glass        """
236*4281ad8eSSimon Glass        self._make_flags['target'] = board.target
237*4281ad8eSSimon Glass        arg_str = self.ResolveReferences(self._make_flags,
238*4281ad8eSSimon Glass                           self._make_flags.get(board.target, ''))
239*4281ad8eSSimon Glass        args = arg_str.split(' ')
240*4281ad8eSSimon Glass        i = 0
241*4281ad8eSSimon Glass        while i < len(args):
242*4281ad8eSSimon Glass            if not args[i]:
243*4281ad8eSSimon Glass                del args[i]
244*4281ad8eSSimon Glass            else:
245*4281ad8eSSimon Glass                i += 1
246*4281ad8eSSimon Glass        return args
247