xref: /rk3399_rockchip-uboot/tools/buildman/toolchain.py (revision 80e6a487505c44bffbf5bf97cfa5ce2176e0cd9b)
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
8827e37b5SSimon Glassfrom HTMLParser import HTMLParser
9fc3fe1c2SSimon Glassimport os
10827e37b5SSimon Glassimport sys
11827e37b5SSimon Glassimport tempfile
12827e37b5SSimon Glassimport urllib2
13fc3fe1c2SSimon Glass
14fc3fe1c2SSimon Glassimport bsettings
15fc3fe1c2SSimon Glassimport command
16fc3fe1c2SSimon Glass
1717bce66cSSimon Glass(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH,
1817bce66cSSimon Glass    PRIORITY_CALC) = range(4)
19ff690df9SSimon Glass
20827e37b5SSimon Glass# Simple class to collect links from a page
21827e37b5SSimon Glassclass MyHTMLParser(HTMLParser):
22827e37b5SSimon Glass    def __init__(self, arch):
23827e37b5SSimon Glass        """Create a new parser
24827e37b5SSimon Glass
25827e37b5SSimon Glass        After the parser runs, self.links will be set to a list of the links
26827e37b5SSimon Glass        to .xz archives found in the page, and self.arch_link will be set to
27827e37b5SSimon Glass        the one for the given architecture (or None if not found).
28827e37b5SSimon Glass
29827e37b5SSimon Glass        Args:
30827e37b5SSimon Glass            arch: Architecture to search for
31827e37b5SSimon Glass        """
32827e37b5SSimon Glass        HTMLParser.__init__(self)
33827e37b5SSimon Glass        self.arch_link = None
34827e37b5SSimon Glass        self.links = []
35827e37b5SSimon Glass        self._match = '_%s-' % arch
36827e37b5SSimon Glass
37827e37b5SSimon Glass    def handle_starttag(self, tag, attrs):
38827e37b5SSimon Glass        if tag == 'a':
39827e37b5SSimon Glass            for tag, value in attrs:
40827e37b5SSimon Glass                if tag == 'href':
41827e37b5SSimon Glass                    if value and value.endswith('.xz'):
42827e37b5SSimon Glass                        self.links.append(value)
43827e37b5SSimon Glass                        if self._match in value:
44827e37b5SSimon Glass                            self.arch_link = value
45827e37b5SSimon Glass
46827e37b5SSimon Glass
47fc3fe1c2SSimon Glassclass Toolchain:
48fc3fe1c2SSimon Glass    """A single toolchain
49fc3fe1c2SSimon Glass
50fc3fe1c2SSimon Glass    Public members:
51fc3fe1c2SSimon Glass        gcc: Full path to C compiler
52fc3fe1c2SSimon Glass        path: Directory path containing C compiler
53fc3fe1c2SSimon Glass        cross: Cross compile string, e.g. 'arm-linux-'
54fc3fe1c2SSimon Glass        arch: Architecture of toolchain as determined from the first
55fc3fe1c2SSimon Glass                component of the filename. E.g. arm-linux-gcc becomes arm
56ff690df9SSimon Glass        priority: Toolchain priority (0=highest, 20=lowest)
57fc3fe1c2SSimon Glass    """
58608e399fSSimon Glass    def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC,
59608e399fSSimon Glass                 arch=None):
60fc3fe1c2SSimon Glass        """Create a new toolchain object.
61fc3fe1c2SSimon Glass
62fc3fe1c2SSimon Glass        Args:
63fc3fe1c2SSimon Glass            fname: Filename of the gcc component
64fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
65ad24ebacSSimon Glass            verbose: True to print out the information
66ff690df9SSimon Glass            priority: Priority to use for this toolchain, or PRIORITY_CALC to
67ff690df9SSimon Glass                calculate it
68fc3fe1c2SSimon Glass        """
69fc3fe1c2SSimon Glass        self.gcc = fname
70fc3fe1c2SSimon Glass        self.path = os.path.dirname(fname)
71b5324123SSimon Glass
72b5324123SSimon Glass        # Find the CROSS_COMPILE prefix to use for U-Boot. For example,
73b5324123SSimon Glass        # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'.
74b5324123SSimon Glass        basename = os.path.basename(fname)
75b5324123SSimon Glass        pos = basename.rfind('-')
76b5324123SSimon Glass        self.cross = basename[:pos + 1] if pos != -1 else ''
77b5324123SSimon Glass
78b5324123SSimon Glass        # The architecture is the first part of the name
79fc3fe1c2SSimon Glass        pos = self.cross.find('-')
80608e399fSSimon Glass        if arch:
81608e399fSSimon Glass            self.arch = arch
82608e399fSSimon Glass        else:
83fc3fe1c2SSimon Glass            self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
84fc3fe1c2SSimon Glass
85bb1501f2SSimon Glass        env = self.MakeEnvironment(False)
86fc3fe1c2SSimon Glass
87fc3fe1c2SSimon Glass        # As a basic sanity check, run the C compiler with --version
88fc3fe1c2SSimon Glass        cmd = [fname, '--version']
89ff690df9SSimon Glass        if priority == PRIORITY_CALC:
90ff690df9SSimon Glass            self.priority = self.GetPriority(fname)
91ff690df9SSimon Glass        else:
92ff690df9SSimon Glass            self.priority = priority
93fc3fe1c2SSimon Glass        if test:
948bb2bddcSStephen Warren            result = command.RunPipe([cmd], capture=True, env=env,
958bb2bddcSStephen Warren                                     raise_on_error=False)
96fc3fe1c2SSimon Glass            self.ok = result.return_code == 0
97fc3fe1c2SSimon Glass            if verbose:
98fc3fe1c2SSimon Glass                print 'Tool chain test: ',
99fc3fe1c2SSimon Glass                if self.ok:
100608e399fSSimon Glass                    print "OK, arch='%s', priority %d" % (self.arch,
101608e399fSSimon Glass                                                          self.priority)
102fc3fe1c2SSimon Glass                else:
103fc3fe1c2SSimon Glass                    print 'BAD'
104fc3fe1c2SSimon Glass                    print 'Command: ', cmd
105fc3fe1c2SSimon Glass                    print result.stdout
106fc3fe1c2SSimon Glass                    print result.stderr
107fc3fe1c2SSimon Glass        else:
108fc3fe1c2SSimon Glass            self.ok = True
109fc3fe1c2SSimon Glass
110fc3fe1c2SSimon Glass    def GetPriority(self, fname):
111fc3fe1c2SSimon Glass        """Return the priority of the toolchain.
112fc3fe1c2SSimon Glass
113fc3fe1c2SSimon Glass        Toolchains are ranked according to their suitability by their
114fc3fe1c2SSimon Glass        filename prefix.
115fc3fe1c2SSimon Glass
116fc3fe1c2SSimon Glass        Args:
117fc3fe1c2SSimon Glass            fname: Filename of toolchain
118fc3fe1c2SSimon Glass        Returns:
119ff690df9SSimon Glass            Priority of toolchain, PRIORITY_CALC=highest, 20=lowest.
120fc3fe1c2SSimon Glass        """
1218708267fSMasahiro Yamada        priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
122fc3fe1c2SSimon Glass            '-none-linux-gnueabi', '-uclinux', '-none-eabi',
123fc3fe1c2SSimon Glass            '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
124fc3fe1c2SSimon Glass        for prio in range(len(priority_list)):
125fc3fe1c2SSimon Glass            if priority_list[prio] in fname:
126ff690df9SSimon Glass                return PRIORITY_CALC + prio
127ff690df9SSimon Glass        return PRIORITY_CALC + prio
128fc3fe1c2SSimon Glass
129bb1501f2SSimon Glass    def MakeEnvironment(self, full_path):
130fc3fe1c2SSimon Glass        """Returns an environment for using the toolchain.
131fc3fe1c2SSimon Glass
132bb1501f2SSimon Glass        Thie takes the current environment and adds CROSS_COMPILE so that
133bb1501f2SSimon Glass        the tool chain will operate correctly.
134bb1501f2SSimon Glass
135bb1501f2SSimon Glass        Args:
136bb1501f2SSimon Glass            full_path: Return the full path in CROSS_COMPILE and don't set
137bb1501f2SSimon Glass                PATH
138fc3fe1c2SSimon Glass        """
139fc3fe1c2SSimon Glass        env = dict(os.environ)
140bb1501f2SSimon Glass        if full_path:
141bb1501f2SSimon Glass            env['CROSS_COMPILE'] = os.path.join(self.path, self.cross)
142bb1501f2SSimon Glass        else:
143fc3fe1c2SSimon Glass            env['CROSS_COMPILE'] = self.cross
144f210b587SSimon Glass            env['PATH'] = self.path + ':' + env['PATH']
145bb1501f2SSimon Glass
146fc3fe1c2SSimon Glass        return env
147fc3fe1c2SSimon Glass
148fc3fe1c2SSimon Glass
149fc3fe1c2SSimon Glassclass Toolchains:
150fc3fe1c2SSimon Glass    """Manage a list of toolchains for building U-Boot
151fc3fe1c2SSimon Glass
152fc3fe1c2SSimon Glass    We select one toolchain for each architecture type
153fc3fe1c2SSimon Glass
154fc3fe1c2SSimon Glass    Public members:
155fc3fe1c2SSimon Glass        toolchains: Dict of Toolchain objects, keyed by architecture name
15617bce66cSSimon Glass        prefixes: Dict of prefixes to check, keyed by architecture. This can
15717bce66cSSimon Glass            be a full path and toolchain prefix, for example
15817bce66cSSimon Glass            {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of
15917bce66cSSimon Glass            something on the search path, for example
16017bce66cSSimon Glass            {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported.
161fc3fe1c2SSimon Glass        paths: List of paths to check for toolchains (may contain wildcards)
162fc3fe1c2SSimon Glass    """
163fc3fe1c2SSimon Glass
164fc3fe1c2SSimon Glass    def __init__(self):
165fc3fe1c2SSimon Glass        self.toolchains = {}
16617bce66cSSimon Glass        self.prefixes = {}
167fc3fe1c2SSimon Glass        self.paths = []
168d4144e45SSimon Glass        self._make_flags = dict(bsettings.GetItems('make-flags'))
169d4144e45SSimon Glass
170*80e6a487SSimon Glass    def GetPathList(self, show_warning=True):
171827e37b5SSimon Glass        """Get a list of available toolchain paths
172827e37b5SSimon Glass
173*80e6a487SSimon Glass        Args:
174*80e6a487SSimon Glass            show_warning: True to show a warning if there are no tool chains.
175*80e6a487SSimon Glass
176827e37b5SSimon Glass        Returns:
177827e37b5SSimon Glass            List of strings, each a path to a toolchain mentioned in the
178827e37b5SSimon Glass            [toolchain] section of the settings file.
179827e37b5SSimon Glass        """
1804281ad8eSSimon Glass        toolchains = bsettings.GetItems('toolchain')
181*80e6a487SSimon Glass        if show_warning and not toolchains:
182ad24ebacSSimon Glass            print ('Warning: No tool chains - please add a [toolchain] section'
183ad24ebacSSimon Glass                 ' to your buildman config file %s. See README for details' %
1841826a18dSMasahiro Yamada                 bsettings.config_fname)
1854281ad8eSSimon Glass
186827e37b5SSimon Glass        paths = []
1874281ad8eSSimon Glass        for name, value in toolchains:
188fc3fe1c2SSimon Glass            if '*' in value:
189827e37b5SSimon Glass                paths += glob.glob(value)
190fc3fe1c2SSimon Glass            else:
191827e37b5SSimon Glass                paths.append(value)
192827e37b5SSimon Glass        return paths
193827e37b5SSimon Glass
194*80e6a487SSimon Glass    def GetSettings(self, show_warning=True):
195*80e6a487SSimon Glass        """Get toolchain settings from the settings file.
196*80e6a487SSimon Glass
197*80e6a487SSimon Glass        Args:
198*80e6a487SSimon Glass            show_warning: True to show a warning if there are no tool chains.
199*80e6a487SSimon Glass        """
20017bce66cSSimon Glass        self.prefixes = bsettings.GetItems('toolchain-prefix')
201*80e6a487SSimon Glass        self.paths += self.GetPathList(show_warning)
202fc3fe1c2SSimon Glass
203608e399fSSimon Glass    def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
204608e399fSSimon Glass            arch=None):
205fc3fe1c2SSimon Glass        """Add a toolchain to our list
206fc3fe1c2SSimon Glass
207fc3fe1c2SSimon Glass        We select the given toolchain as our preferred one for its
208fc3fe1c2SSimon Glass        architecture if it is a higher priority than the others.
209fc3fe1c2SSimon Glass
210fc3fe1c2SSimon Glass        Args:
211fc3fe1c2SSimon Glass            fname: Filename of toolchain's gcc driver
212fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
213ff690df9SSimon Glass            priority: Priority to use for this toolchain
214608e399fSSimon Glass            arch: Toolchain architecture, or None if not known
215fc3fe1c2SSimon Glass        """
216608e399fSSimon Glass        toolchain = Toolchain(fname, test, verbose, priority, arch)
217fc3fe1c2SSimon Glass        add_it = toolchain.ok
218fc3fe1c2SSimon Glass        if toolchain.arch in self.toolchains:
219fc3fe1c2SSimon Glass            add_it = (toolchain.priority <
220fc3fe1c2SSimon Glass                        self.toolchains[toolchain.arch].priority)
221fc3fe1c2SSimon Glass        if add_it:
222fc3fe1c2SSimon Glass            self.toolchains[toolchain.arch] = toolchain
223ff690df9SSimon Glass        elif verbose:
224ff690df9SSimon Glass            print ("Toolchain '%s' at priority %d will be ignored because "
225ff690df9SSimon Glass                   "another toolchain for arch '%s' has priority %d" %
226ff690df9SSimon Glass                   (toolchain.gcc, toolchain.priority, toolchain.arch,
227ff690df9SSimon Glass                    self.toolchains[toolchain.arch].priority))
228fc3fe1c2SSimon Glass
229827e37b5SSimon Glass    def ScanPath(self, path, verbose):
230827e37b5SSimon Glass        """Scan a path for a valid toolchain
231827e37b5SSimon Glass
232827e37b5SSimon Glass        Args:
233827e37b5SSimon Glass            path: Path to scan
234827e37b5SSimon Glass            verbose: True to print out progress information
235827e37b5SSimon Glass        Returns:
236827e37b5SSimon Glass            Filename of C compiler if found, else None
237827e37b5SSimon Glass        """
238d9088983SAlbert ARIBAUD        fnames = []
239827e37b5SSimon Glass        for subdir in ['.', 'bin', 'usr/bin']:
240827e37b5SSimon Glass            dirname = os.path.join(path, subdir)
241827e37b5SSimon Glass            if verbose: print "      - looking in '%s'" % dirname
242827e37b5SSimon Glass            for fname in glob.glob(dirname + '/*gcc'):
243827e37b5SSimon Glass                if verbose: print "         - found '%s'" % fname
244d9088983SAlbert ARIBAUD                fnames.append(fname)
245d9088983SAlbert ARIBAUD        return fnames
246827e37b5SSimon Glass
24717bce66cSSimon Glass    def ScanPathEnv(self, fname):
24817bce66cSSimon Glass        """Scan the PATH environment variable for a given filename.
24917bce66cSSimon Glass
25017bce66cSSimon Glass        Args:
25117bce66cSSimon Glass            fname: Filename to scan for
25217bce66cSSimon Glass        Returns:
25317bce66cSSimon Glass            List of matching pathanames, or [] if none
25417bce66cSSimon Glass        """
25517bce66cSSimon Glass        pathname_list = []
25617bce66cSSimon Glass        for path in os.environ["PATH"].split(os.pathsep):
25717bce66cSSimon Glass            path = path.strip('"')
25817bce66cSSimon Glass            pathname = os.path.join(path, fname)
25917bce66cSSimon Glass            if os.path.exists(pathname):
26017bce66cSSimon Glass                pathname_list.append(pathname)
26117bce66cSSimon Glass        return pathname_list
262827e37b5SSimon Glass
263fc3fe1c2SSimon Glass    def Scan(self, verbose):
264fc3fe1c2SSimon Glass        """Scan for available toolchains and select the best for each arch.
265fc3fe1c2SSimon Glass
266fc3fe1c2SSimon Glass        We look for all the toolchains we can file, figure out the
267fc3fe1c2SSimon Glass        architecture for each, and whether it works. Then we select the
268fc3fe1c2SSimon Glass        highest priority toolchain for each arch.
269fc3fe1c2SSimon Glass
270fc3fe1c2SSimon Glass        Args:
271fc3fe1c2SSimon Glass            verbose: True to print out progress information
272fc3fe1c2SSimon Glass        """
273fc3fe1c2SSimon Glass        if verbose: print 'Scanning for tool chains'
27417bce66cSSimon Glass        for name, value in self.prefixes:
27517bce66cSSimon Glass            if verbose: print "   - scanning prefix '%s'" % value
27617bce66cSSimon Glass            if os.path.exists(value):
27717bce66cSSimon Glass                self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name)
27817bce66cSSimon Glass                continue
27917bce66cSSimon Glass            fname = value + 'gcc'
28017bce66cSSimon Glass            if os.path.exists(fname):
28117bce66cSSimon Glass                self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name)
28217bce66cSSimon Glass                continue
28317bce66cSSimon Glass            fname_list = self.ScanPathEnv(fname)
28417bce66cSSimon Glass            for f in fname_list:
28517bce66cSSimon Glass                self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name)
28617bce66cSSimon Glass            if not fname_list:
28717bce66cSSimon Glass                raise ValueError, ("No tool chain found for prefix '%s'" %
28817bce66cSSimon Glass                                   value)
289fc3fe1c2SSimon Glass        for path in self.paths:
290fc3fe1c2SSimon Glass            if verbose: print "   - scanning path '%s'" % path
291d9088983SAlbert ARIBAUD            fnames = self.ScanPath(path, verbose)
292d9088983SAlbert ARIBAUD            for fname in fnames:
293fc3fe1c2SSimon Glass                self.Add(fname, True, verbose)
294fc3fe1c2SSimon Glass
295fc3fe1c2SSimon Glass    def List(self):
296fc3fe1c2SSimon Glass        """List out the selected toolchains for each architecture"""
297fc3fe1c2SSimon Glass        print 'List of available toolchains (%d):' % len(self.toolchains)
298fc3fe1c2SSimon Glass        if len(self.toolchains):
299fc3fe1c2SSimon Glass            for key, value in sorted(self.toolchains.iteritems()):
300fc3fe1c2SSimon Glass                print '%-10s: %s' % (key, value.gcc)
301fc3fe1c2SSimon Glass        else:
302fc3fe1c2SSimon Glass            print 'None'
303fc3fe1c2SSimon Glass
304fc3fe1c2SSimon Glass    def Select(self, arch):
305fc3fe1c2SSimon Glass        """Returns the toolchain for a given architecture
306fc3fe1c2SSimon Glass
307fc3fe1c2SSimon Glass        Args:
308fc3fe1c2SSimon Glass            args: Name of architecture (e.g. 'arm', 'ppc_8xx')
309fc3fe1c2SSimon Glass
310fc3fe1c2SSimon Glass        returns:
311fc3fe1c2SSimon Glass            toolchain object, or None if none found
312fc3fe1c2SSimon Glass        """
3139b83bfdcSSimon Glass        for tag, value in bsettings.GetItems('toolchain-alias'):
3149b83bfdcSSimon Glass            if arch == tag:
3159b83bfdcSSimon Glass                for alias in value.split():
3169b83bfdcSSimon Glass                    if alias in self.toolchains:
3179b83bfdcSSimon Glass                        return self.toolchains[alias]
318fc3fe1c2SSimon Glass
319fc3fe1c2SSimon Glass        if not arch in self.toolchains:
320fc3fe1c2SSimon Glass            raise ValueError, ("No tool chain found for arch '%s'" % arch)
321fc3fe1c2SSimon Glass        return self.toolchains[arch]
3224281ad8eSSimon Glass
3234281ad8eSSimon Glass    def ResolveReferences(self, var_dict, args):
3244281ad8eSSimon Glass        """Resolve variable references in a string
3254281ad8eSSimon Glass
3264281ad8eSSimon Glass        This converts ${blah} within the string to the value of blah.
3274281ad8eSSimon Glass        This function works recursively.
3284281ad8eSSimon Glass
3294281ad8eSSimon Glass        Args:
3304281ad8eSSimon Glass            var_dict: Dictionary containing variables and their values
3314281ad8eSSimon Glass            args: String containing make arguments
3324281ad8eSSimon Glass        Returns:
3334281ad8eSSimon Glass            Resolved string
3344281ad8eSSimon Glass
3354281ad8eSSimon Glass        >>> bsettings.Setup()
3364281ad8eSSimon Glass        >>> tcs = Toolchains()
3374281ad8eSSimon Glass        >>> tcs.Add('fred', False)
3384281ad8eSSimon Glass        >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
3394281ad8eSSimon Glass                        'second' : '2nd'}
3404281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
3414281ad8eSSimon Glass        'this=OBLIQUE_set'
3424281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
3434281ad8eSSimon Glass        'this=OBLIQUE_setfi2ndrstnd'
3444281ad8eSSimon Glass        """
345f60c9d4fSSimon Glass        re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
3464281ad8eSSimon Glass
3474281ad8eSSimon Glass        while True:
3484281ad8eSSimon Glass            m = re_var.search(args)
3494281ad8eSSimon Glass            if not m:
3504281ad8eSSimon Glass                break
3514281ad8eSSimon Glass            lookup = m.group(0)[2:-1]
3524281ad8eSSimon Glass            value = var_dict.get(lookup, '')
3534281ad8eSSimon Glass            args = args[:m.start(0)] + value + args[m.end(0):]
3544281ad8eSSimon Glass        return args
3554281ad8eSSimon Glass
3564281ad8eSSimon Glass    def GetMakeArguments(self, board):
3574281ad8eSSimon Glass        """Returns 'make' arguments for a given board
3584281ad8eSSimon Glass
3594281ad8eSSimon Glass        The flags are in a section called 'make-flags'. Flags are named
3604281ad8eSSimon Glass        after the target they represent, for example snapper9260=TESTING=1
3614281ad8eSSimon Glass        will pass TESTING=1 to make when building the snapper9260 board.
3624281ad8eSSimon Glass
3634281ad8eSSimon Glass        References to other boards can be added in the string also. For
3644281ad8eSSimon Glass        example:
3654281ad8eSSimon Glass
3664281ad8eSSimon Glass        [make-flags]
3674281ad8eSSimon Glass        at91-boards=ENABLE_AT91_TEST=1
3684281ad8eSSimon Glass        snapper9260=${at91-boards} BUILD_TAG=442
3694281ad8eSSimon Glass        snapper9g45=${at91-boards} BUILD_TAG=443
3704281ad8eSSimon Glass
3714281ad8eSSimon Glass        This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
3724281ad8eSSimon Glass        and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
3734281ad8eSSimon Glass
3744281ad8eSSimon Glass        A special 'target' variable is set to the board target.
3754281ad8eSSimon Glass
3764281ad8eSSimon Glass        Args:
3774281ad8eSSimon Glass            board: Board object for the board to check.
3784281ad8eSSimon Glass        Returns:
3794281ad8eSSimon Glass            'make' flags for that board, or '' if none
3804281ad8eSSimon Glass        """
3814281ad8eSSimon Glass        self._make_flags['target'] = board.target
3824281ad8eSSimon Glass        arg_str = self.ResolveReferences(self._make_flags,
3834281ad8eSSimon Glass                           self._make_flags.get(board.target, ''))
3844281ad8eSSimon Glass        args = arg_str.split(' ')
3854281ad8eSSimon Glass        i = 0
3864281ad8eSSimon Glass        while i < len(args):
3874281ad8eSSimon Glass            if not args[i]:
3884281ad8eSSimon Glass                del args[i]
3894281ad8eSSimon Glass            else:
3904281ad8eSSimon Glass                i += 1
3914281ad8eSSimon Glass        return args
392827e37b5SSimon Glass
393827e37b5SSimon Glass    def LocateArchUrl(self, fetch_arch):
394827e37b5SSimon Glass        """Find a toolchain available online
395827e37b5SSimon Glass
396827e37b5SSimon Glass        Look in standard places for available toolchains. At present the
397827e37b5SSimon Glass        only standard place is at kernel.org.
398827e37b5SSimon Glass
399827e37b5SSimon Glass        Args:
400827e37b5SSimon Glass            arch: Architecture to look for, or 'list' for all
401827e37b5SSimon Glass        Returns:
402827e37b5SSimon Glass            If fetch_arch is 'list', a tuple:
403827e37b5SSimon Glass                Machine architecture (e.g. x86_64)
404827e37b5SSimon Glass                List of toolchains
405827e37b5SSimon Glass            else
406827e37b5SSimon Glass                URL containing this toolchain, if avaialble, else None
407827e37b5SSimon Glass        """
408827e37b5SSimon Glass        arch = command.OutputOneLine('uname', '-m')
409827e37b5SSimon Glass        base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
4101246231cSMichal Simek        versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4']
411827e37b5SSimon Glass        links = []
412827e37b5SSimon Glass        for version in versions:
413827e37b5SSimon Glass            url = '%s/%s/%s/' % (base, arch, version)
414827e37b5SSimon Glass            print 'Checking: %s' % url
415827e37b5SSimon Glass            response = urllib2.urlopen(url)
416827e37b5SSimon Glass            html = response.read()
417827e37b5SSimon Glass            parser = MyHTMLParser(fetch_arch)
418827e37b5SSimon Glass            parser.feed(html)
419827e37b5SSimon Glass            if fetch_arch == 'list':
420827e37b5SSimon Glass                links += parser.links
421827e37b5SSimon Glass            elif parser.arch_link:
422827e37b5SSimon Glass                return url + parser.arch_link
423827e37b5SSimon Glass        if fetch_arch == 'list':
424827e37b5SSimon Glass            return arch, links
425827e37b5SSimon Glass        return None
426827e37b5SSimon Glass
427827e37b5SSimon Glass    def Download(self, url):
428827e37b5SSimon Glass        """Download a file to a temporary directory
429827e37b5SSimon Glass
430827e37b5SSimon Glass        Args:
431827e37b5SSimon Glass            url: URL to download
432827e37b5SSimon Glass        Returns:
433827e37b5SSimon Glass            Tuple:
434827e37b5SSimon Glass                Temporary directory name
435827e37b5SSimon Glass                Full path to the downloaded archive file in that directory,
436827e37b5SSimon Glass                    or None if there was an error while downloading
437827e37b5SSimon Glass        """
438ad24ebacSSimon Glass        print 'Downloading: %s' % url
439827e37b5SSimon Glass        leaf = url.split('/')[-1]
440827e37b5SSimon Glass        tmpdir = tempfile.mkdtemp('.buildman')
441827e37b5SSimon Glass        response = urllib2.urlopen(url)
442827e37b5SSimon Glass        fname = os.path.join(tmpdir, leaf)
443827e37b5SSimon Glass        fd = open(fname, 'wb')
444827e37b5SSimon Glass        meta = response.info()
445ad24ebacSSimon Glass        size = int(meta.getheaders('Content-Length')[0])
446827e37b5SSimon Glass        done = 0
447827e37b5SSimon Glass        block_size = 1 << 16
448827e37b5SSimon Glass        status = ''
449827e37b5SSimon Glass
450827e37b5SSimon Glass        # Read the file in chunks and show progress as we go
451827e37b5SSimon Glass        while True:
452827e37b5SSimon Glass            buffer = response.read(block_size)
453827e37b5SSimon Glass            if not buffer:
454827e37b5SSimon Glass                print chr(8) * (len(status) + 1), '\r',
455827e37b5SSimon Glass                break
456827e37b5SSimon Glass
457827e37b5SSimon Glass            done += len(buffer)
458827e37b5SSimon Glass            fd.write(buffer)
459ad24ebacSSimon Glass            status = r'%10d MiB  [%3d%%]' % (done / 1024 / 1024,
460827e37b5SSimon Glass                                             done * 100 / size)
461827e37b5SSimon Glass            status = status + chr(8) * (len(status) + 1)
462827e37b5SSimon Glass            print status,
463827e37b5SSimon Glass            sys.stdout.flush()
464827e37b5SSimon Glass        fd.close()
465827e37b5SSimon Glass        if done != size:
466827e37b5SSimon Glass            print 'Error, failed to download'
467827e37b5SSimon Glass            os.remove(fname)
468827e37b5SSimon Glass            fname = None
469827e37b5SSimon Glass        return tmpdir, fname
470827e37b5SSimon Glass
471827e37b5SSimon Glass    def Unpack(self, fname, dest):
472827e37b5SSimon Glass        """Unpack a tar file
473827e37b5SSimon Glass
474827e37b5SSimon Glass        Args:
475827e37b5SSimon Glass            fname: Filename to unpack
476827e37b5SSimon Glass            dest: Destination directory
477827e37b5SSimon Glass        Returns:
478827e37b5SSimon Glass            Directory name of the first entry in the archive, without the
479827e37b5SSimon Glass            trailing /
480827e37b5SSimon Glass        """
481827e37b5SSimon Glass        stdout = command.Output('tar', 'xvfJ', fname, '-C', dest)
482827e37b5SSimon Glass        return stdout.splitlines()[0][:-1]
483827e37b5SSimon Glass
484827e37b5SSimon Glass    def TestSettingsHasPath(self, path):
485827e37b5SSimon Glass        """Check if builmand will find this toolchain
486827e37b5SSimon Glass
487827e37b5SSimon Glass        Returns:
488827e37b5SSimon Glass            True if the path is in settings, False if not
489827e37b5SSimon Glass        """
490*80e6a487SSimon Glass        paths = self.GetPathList(False)
491827e37b5SSimon Glass        return path in paths
492827e37b5SSimon Glass
493827e37b5SSimon Glass    def ListArchs(self):
494827e37b5SSimon Glass        """List architectures with available toolchains to download"""
495827e37b5SSimon Glass        host_arch, archives = self.LocateArchUrl('list')
496827e37b5SSimon Glass        re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*')
497827e37b5SSimon Glass        arch_set = set()
498827e37b5SSimon Glass        for archive in archives:
499827e37b5SSimon Glass            # Remove the host architecture from the start
500827e37b5SSimon Glass            arch = re_arch.match(archive[len(host_arch):])
501827e37b5SSimon Glass            if arch:
502827e37b5SSimon Glass                arch_set.add(arch.group(1))
503827e37b5SSimon Glass        return sorted(arch_set)
504827e37b5SSimon Glass
505827e37b5SSimon Glass    def FetchAndInstall(self, arch):
506827e37b5SSimon Glass        """Fetch and install a new toolchain
507827e37b5SSimon Glass
508827e37b5SSimon Glass        arch:
509827e37b5SSimon Glass            Architecture to fetch, or 'list' to list
510827e37b5SSimon Glass        """
511827e37b5SSimon Glass        # Fist get the URL for this architecture
512827e37b5SSimon Glass        url = self.LocateArchUrl(arch)
513827e37b5SSimon Glass        if not url:
514827e37b5SSimon Glass            print ("Cannot find toolchain for arch '%s' - use 'list' to list" %
515827e37b5SSimon Glass                   arch)
516827e37b5SSimon Glass            return 2
517827e37b5SSimon Glass        home = os.environ['HOME']
518827e37b5SSimon Glass        dest = os.path.join(home, '.buildman-toolchains')
519827e37b5SSimon Glass        if not os.path.exists(dest):
520827e37b5SSimon Glass            os.mkdir(dest)
521827e37b5SSimon Glass
522827e37b5SSimon Glass        # Download the tar file for this toolchain and unpack it
523827e37b5SSimon Glass        tmpdir, tarfile = self.Download(url)
524827e37b5SSimon Glass        if not tarfile:
525827e37b5SSimon Glass            return 1
526827e37b5SSimon Glass        print 'Unpacking to: %s' % dest,
527827e37b5SSimon Glass        sys.stdout.flush()
528827e37b5SSimon Glass        path = self.Unpack(tarfile, dest)
529827e37b5SSimon Glass        os.remove(tarfile)
530827e37b5SSimon Glass        os.rmdir(tmpdir)
531827e37b5SSimon Glass        print
532827e37b5SSimon Glass
533827e37b5SSimon Glass        # Check that the toolchain works
534827e37b5SSimon Glass        print 'Testing'
535827e37b5SSimon Glass        dirpath = os.path.join(dest, path)
5362a76a649SSimon Glass        compiler_fname_list = self.ScanPath(dirpath, True)
5372a76a649SSimon Glass        if not compiler_fname_list:
538827e37b5SSimon Glass            print 'Could not locate C compiler - fetch failed.'
539827e37b5SSimon Glass            return 1
5402a76a649SSimon Glass        if len(compiler_fname_list) != 1:
5412a76a649SSimon Glass            print ('Internal error, ambiguous toolchains: %s' %
5422a76a649SSimon Glass                   (', '.join(compiler_fname)))
5432a76a649SSimon Glass            return 1
5442a76a649SSimon Glass        toolchain = Toolchain(compiler_fname_list[0], True, True)
545827e37b5SSimon Glass
546827e37b5SSimon Glass        # Make sure that it will be found by buildman
547827e37b5SSimon Glass        if not self.TestSettingsHasPath(dirpath):
548827e37b5SSimon Glass            print ("Adding 'download' to config file '%s'" %
549827e37b5SSimon Glass                   bsettings.config_fname)
550827e37b5SSimon Glass            tools_dir = os.path.dirname(dirpath)
551827e37b5SSimon Glass            bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir)
552827e37b5SSimon Glass        return 0
553