xref: /rk3399_rockchip-uboot/tools/buildman/toolchain.py (revision ad24ebace118f4145b1a3f782ddf9b9ce5f8dcf3)
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
17827e37b5SSimon Glass# Simple class to collect links from a page
18827e37b5SSimon Glassclass MyHTMLParser(HTMLParser):
19827e37b5SSimon Glass    def __init__(self, arch):
20827e37b5SSimon Glass        """Create a new parser
21827e37b5SSimon Glass
22827e37b5SSimon Glass        After the parser runs, self.links will be set to a list of the links
23827e37b5SSimon Glass        to .xz archives found in the page, and self.arch_link will be set to
24827e37b5SSimon Glass        the one for the given architecture (or None if not found).
25827e37b5SSimon Glass
26827e37b5SSimon Glass        Args:
27827e37b5SSimon Glass            arch: Architecture to search for
28827e37b5SSimon Glass        """
29827e37b5SSimon Glass        HTMLParser.__init__(self)
30827e37b5SSimon Glass        self.arch_link = None
31827e37b5SSimon Glass        self.links = []
32827e37b5SSimon Glass        self._match = '_%s-' % arch
33827e37b5SSimon Glass
34827e37b5SSimon Glass    def handle_starttag(self, tag, attrs):
35827e37b5SSimon Glass        if tag == 'a':
36827e37b5SSimon Glass            for tag, value in attrs:
37827e37b5SSimon Glass                if tag == 'href':
38827e37b5SSimon Glass                    if value and value.endswith('.xz'):
39827e37b5SSimon Glass                        self.links.append(value)
40827e37b5SSimon Glass                        if self._match in value:
41827e37b5SSimon Glass                            self.arch_link = value
42827e37b5SSimon Glass
43827e37b5SSimon Glass
44fc3fe1c2SSimon Glassclass Toolchain:
45fc3fe1c2SSimon Glass    """A single toolchain
46fc3fe1c2SSimon Glass
47fc3fe1c2SSimon Glass    Public members:
48fc3fe1c2SSimon Glass        gcc: Full path to C compiler
49fc3fe1c2SSimon Glass        path: Directory path containing C compiler
50fc3fe1c2SSimon Glass        cross: Cross compile string, e.g. 'arm-linux-'
51fc3fe1c2SSimon Glass        arch: Architecture of toolchain as determined from the first
52fc3fe1c2SSimon Glass                component of the filename. E.g. arm-linux-gcc becomes arm
53fc3fe1c2SSimon Glass    """
54fc3fe1c2SSimon Glass    def __init__(self, fname, test, verbose=False):
55fc3fe1c2SSimon Glass        """Create a new toolchain object.
56fc3fe1c2SSimon Glass
57fc3fe1c2SSimon Glass        Args:
58fc3fe1c2SSimon Glass            fname: Filename of the gcc component
59fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
60*ad24ebacSSimon Glass            verbose: True to print out the information
61fc3fe1c2SSimon Glass        """
62fc3fe1c2SSimon Glass        self.gcc = fname
63fc3fe1c2SSimon Glass        self.path = os.path.dirname(fname)
64b5324123SSimon Glass
65b5324123SSimon Glass        # Find the CROSS_COMPILE prefix to use for U-Boot. For example,
66b5324123SSimon Glass        # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'.
67b5324123SSimon Glass        basename = os.path.basename(fname)
68b5324123SSimon Glass        pos = basename.rfind('-')
69b5324123SSimon Glass        self.cross = basename[:pos + 1] if pos != -1 else ''
70b5324123SSimon Glass
71b5324123SSimon Glass        # The architecture is the first part of the name
72fc3fe1c2SSimon Glass        pos = self.cross.find('-')
73fc3fe1c2SSimon Glass        self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
74fc3fe1c2SSimon Glass
75bb1501f2SSimon Glass        env = self.MakeEnvironment(False)
76fc3fe1c2SSimon Glass
77fc3fe1c2SSimon Glass        # As a basic sanity check, run the C compiler with --version
78fc3fe1c2SSimon Glass        cmd = [fname, '--version']
79fc3fe1c2SSimon Glass        if test:
808bb2bddcSStephen Warren            result = command.RunPipe([cmd], capture=True, env=env,
818bb2bddcSStephen Warren                                     raise_on_error=False)
82fc3fe1c2SSimon Glass            self.ok = result.return_code == 0
83fc3fe1c2SSimon Glass            if verbose:
84fc3fe1c2SSimon Glass                print 'Tool chain test: ',
85fc3fe1c2SSimon Glass                if self.ok:
86fc3fe1c2SSimon Glass                    print 'OK'
87fc3fe1c2SSimon Glass                else:
88fc3fe1c2SSimon Glass                    print 'BAD'
89fc3fe1c2SSimon Glass                    print 'Command: ', cmd
90fc3fe1c2SSimon Glass                    print result.stdout
91fc3fe1c2SSimon Glass                    print result.stderr
92fc3fe1c2SSimon Glass        else:
93fc3fe1c2SSimon Glass            self.ok = True
94fc3fe1c2SSimon Glass        self.priority = self.GetPriority(fname)
95fc3fe1c2SSimon Glass
96fc3fe1c2SSimon Glass    def GetPriority(self, fname):
97fc3fe1c2SSimon Glass        """Return the priority of the toolchain.
98fc3fe1c2SSimon Glass
99fc3fe1c2SSimon Glass        Toolchains are ranked according to their suitability by their
100fc3fe1c2SSimon Glass        filename prefix.
101fc3fe1c2SSimon Glass
102fc3fe1c2SSimon Glass        Args:
103fc3fe1c2SSimon Glass            fname: Filename of toolchain
104fc3fe1c2SSimon Glass        Returns:
105fc3fe1c2SSimon Glass            Priority of toolchain, 0=highest, 20=lowest.
106fc3fe1c2SSimon Glass        """
1078708267fSMasahiro Yamada        priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
108fc3fe1c2SSimon Glass            '-none-linux-gnueabi', '-uclinux', '-none-eabi',
109fc3fe1c2SSimon Glass            '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
110fc3fe1c2SSimon Glass        for prio in range(len(priority_list)):
111fc3fe1c2SSimon Glass            if priority_list[prio] in fname:
112fc3fe1c2SSimon Glass                return prio
113fc3fe1c2SSimon Glass        return prio
114fc3fe1c2SSimon Glass
115bb1501f2SSimon Glass    def MakeEnvironment(self, full_path):
116fc3fe1c2SSimon Glass        """Returns an environment for using the toolchain.
117fc3fe1c2SSimon Glass
118bb1501f2SSimon Glass        Thie takes the current environment and adds CROSS_COMPILE so that
119bb1501f2SSimon Glass        the tool chain will operate correctly.
120bb1501f2SSimon Glass
121bb1501f2SSimon Glass        Args:
122bb1501f2SSimon Glass            full_path: Return the full path in CROSS_COMPILE and don't set
123bb1501f2SSimon Glass                PATH
124fc3fe1c2SSimon Glass        """
125fc3fe1c2SSimon Glass        env = dict(os.environ)
126bb1501f2SSimon Glass        if full_path:
127bb1501f2SSimon Glass            env['CROSS_COMPILE'] = os.path.join(self.path, self.cross)
128bb1501f2SSimon Glass        else:
129fc3fe1c2SSimon Glass            env['CROSS_COMPILE'] = self.cross
130f210b587SSimon Glass            env['PATH'] = self.path + ':' + env['PATH']
131bb1501f2SSimon Glass
132fc3fe1c2SSimon Glass        return env
133fc3fe1c2SSimon Glass
134fc3fe1c2SSimon Glass
135fc3fe1c2SSimon Glassclass Toolchains:
136fc3fe1c2SSimon Glass    """Manage a list of toolchains for building U-Boot
137fc3fe1c2SSimon Glass
138fc3fe1c2SSimon Glass    We select one toolchain for each architecture type
139fc3fe1c2SSimon Glass
140fc3fe1c2SSimon Glass    Public members:
141fc3fe1c2SSimon Glass        toolchains: Dict of Toolchain objects, keyed by architecture name
142fc3fe1c2SSimon Glass        paths: List of paths to check for toolchains (may contain wildcards)
143fc3fe1c2SSimon Glass    """
144fc3fe1c2SSimon Glass
145fc3fe1c2SSimon Glass    def __init__(self):
146fc3fe1c2SSimon Glass        self.toolchains = {}
147fc3fe1c2SSimon Glass        self.paths = []
148d4144e45SSimon Glass        self._make_flags = dict(bsettings.GetItems('make-flags'))
149d4144e45SSimon Glass
150827e37b5SSimon Glass    def GetPathList(self):
151827e37b5SSimon Glass        """Get a list of available toolchain paths
152827e37b5SSimon Glass
153827e37b5SSimon Glass        Returns:
154827e37b5SSimon Glass            List of strings, each a path to a toolchain mentioned in the
155827e37b5SSimon Glass            [toolchain] section of the settings file.
156827e37b5SSimon Glass        """
1574281ad8eSSimon Glass        toolchains = bsettings.GetItems('toolchain')
1584281ad8eSSimon Glass        if not toolchains:
159*ad24ebacSSimon Glass            print ('Warning: No tool chains - please add a [toolchain] section'
160*ad24ebacSSimon Glass                 ' to your buildman config file %s. See README for details' %
1611826a18dSMasahiro Yamada                 bsettings.config_fname)
1624281ad8eSSimon Glass
163827e37b5SSimon Glass        paths = []
1644281ad8eSSimon Glass        for name, value in toolchains:
165fc3fe1c2SSimon Glass            if '*' in value:
166827e37b5SSimon Glass                paths += glob.glob(value)
167fc3fe1c2SSimon Glass            else:
168827e37b5SSimon Glass                paths.append(value)
169827e37b5SSimon Glass        return paths
170827e37b5SSimon Glass
171827e37b5SSimon Glass    def GetSettings(self):
172827e37b5SSimon Glass      self.paths += self.GetPathList()
173fc3fe1c2SSimon Glass
174fc3fe1c2SSimon Glass    def Add(self, fname, test=True, verbose=False):
175fc3fe1c2SSimon Glass        """Add a toolchain to our list
176fc3fe1c2SSimon Glass
177fc3fe1c2SSimon Glass        We select the given toolchain as our preferred one for its
178fc3fe1c2SSimon Glass        architecture if it is a higher priority than the others.
179fc3fe1c2SSimon Glass
180fc3fe1c2SSimon Glass        Args:
181fc3fe1c2SSimon Glass            fname: Filename of toolchain's gcc driver
182fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
183fc3fe1c2SSimon Glass        """
184fc3fe1c2SSimon Glass        toolchain = Toolchain(fname, test, verbose)
185fc3fe1c2SSimon Glass        add_it = toolchain.ok
186fc3fe1c2SSimon Glass        if toolchain.arch in self.toolchains:
187fc3fe1c2SSimon Glass            add_it = (toolchain.priority <
188fc3fe1c2SSimon Glass                        self.toolchains[toolchain.arch].priority)
189fc3fe1c2SSimon Glass        if add_it:
190fc3fe1c2SSimon Glass            self.toolchains[toolchain.arch] = toolchain
191fc3fe1c2SSimon Glass
192827e37b5SSimon Glass    def ScanPath(self, path, verbose):
193827e37b5SSimon Glass        """Scan a path for a valid toolchain
194827e37b5SSimon Glass
195827e37b5SSimon Glass        Args:
196827e37b5SSimon Glass            path: Path to scan
197827e37b5SSimon Glass            verbose: True to print out progress information
198827e37b5SSimon Glass        Returns:
199827e37b5SSimon Glass            Filename of C compiler if found, else None
200827e37b5SSimon Glass        """
201d9088983SAlbert ARIBAUD        fnames = []
202827e37b5SSimon Glass        for subdir in ['.', 'bin', 'usr/bin']:
203827e37b5SSimon Glass            dirname = os.path.join(path, subdir)
204827e37b5SSimon Glass            if verbose: print "      - looking in '%s'" % dirname
205827e37b5SSimon Glass            for fname in glob.glob(dirname + '/*gcc'):
206827e37b5SSimon Glass                if verbose: print "         - found '%s'" % fname
207d9088983SAlbert ARIBAUD                fnames.append(fname)
208d9088983SAlbert ARIBAUD        return fnames
209827e37b5SSimon Glass
210827e37b5SSimon Glass
211fc3fe1c2SSimon Glass    def Scan(self, verbose):
212fc3fe1c2SSimon Glass        """Scan for available toolchains and select the best for each arch.
213fc3fe1c2SSimon Glass
214fc3fe1c2SSimon Glass        We look for all the toolchains we can file, figure out the
215fc3fe1c2SSimon Glass        architecture for each, and whether it works. Then we select the
216fc3fe1c2SSimon Glass        highest priority toolchain for each arch.
217fc3fe1c2SSimon Glass
218fc3fe1c2SSimon Glass        Args:
219fc3fe1c2SSimon Glass            verbose: True to print out progress information
220fc3fe1c2SSimon Glass        """
221fc3fe1c2SSimon Glass        if verbose: print 'Scanning for tool chains'
222fc3fe1c2SSimon Glass        for path in self.paths:
223fc3fe1c2SSimon Glass            if verbose: print "   - scanning path '%s'" % path
224d9088983SAlbert ARIBAUD            fnames = self.ScanPath(path, verbose)
225d9088983SAlbert ARIBAUD            for fname in fnames:
226fc3fe1c2SSimon Glass                self.Add(fname, True, verbose)
227fc3fe1c2SSimon Glass
228fc3fe1c2SSimon Glass    def List(self):
229fc3fe1c2SSimon Glass        """List out the selected toolchains for each architecture"""
230fc3fe1c2SSimon Glass        print 'List of available toolchains (%d):' % len(self.toolchains)
231fc3fe1c2SSimon Glass        if len(self.toolchains):
232fc3fe1c2SSimon Glass            for key, value in sorted(self.toolchains.iteritems()):
233fc3fe1c2SSimon Glass                print '%-10s: %s' % (key, value.gcc)
234fc3fe1c2SSimon Glass        else:
235fc3fe1c2SSimon Glass            print 'None'
236fc3fe1c2SSimon Glass
237fc3fe1c2SSimon Glass    def Select(self, arch):
238fc3fe1c2SSimon Glass        """Returns the toolchain for a given architecture
239fc3fe1c2SSimon Glass
240fc3fe1c2SSimon Glass        Args:
241fc3fe1c2SSimon Glass            args: Name of architecture (e.g. 'arm', 'ppc_8xx')
242fc3fe1c2SSimon Glass
243fc3fe1c2SSimon Glass        returns:
244fc3fe1c2SSimon Glass            toolchain object, or None if none found
245fc3fe1c2SSimon Glass        """
2469b83bfdcSSimon Glass        for tag, value in bsettings.GetItems('toolchain-alias'):
2479b83bfdcSSimon Glass            if arch == tag:
2489b83bfdcSSimon Glass                for alias in value.split():
2499b83bfdcSSimon Glass                    if alias in self.toolchains:
2509b83bfdcSSimon Glass                        return self.toolchains[alias]
251fc3fe1c2SSimon Glass
252fc3fe1c2SSimon Glass        if not arch in self.toolchains:
253fc3fe1c2SSimon Glass            raise ValueError, ("No tool chain found for arch '%s'" % arch)
254fc3fe1c2SSimon Glass        return self.toolchains[arch]
2554281ad8eSSimon Glass
2564281ad8eSSimon Glass    def ResolveReferences(self, var_dict, args):
2574281ad8eSSimon Glass        """Resolve variable references in a string
2584281ad8eSSimon Glass
2594281ad8eSSimon Glass        This converts ${blah} within the string to the value of blah.
2604281ad8eSSimon Glass        This function works recursively.
2614281ad8eSSimon Glass
2624281ad8eSSimon Glass        Args:
2634281ad8eSSimon Glass            var_dict: Dictionary containing variables and their values
2644281ad8eSSimon Glass            args: String containing make arguments
2654281ad8eSSimon Glass        Returns:
2664281ad8eSSimon Glass            Resolved string
2674281ad8eSSimon Glass
2684281ad8eSSimon Glass        >>> bsettings.Setup()
2694281ad8eSSimon Glass        >>> tcs = Toolchains()
2704281ad8eSSimon Glass        >>> tcs.Add('fred', False)
2714281ad8eSSimon Glass        >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
2724281ad8eSSimon Glass                        'second' : '2nd'}
2734281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
2744281ad8eSSimon Glass        'this=OBLIQUE_set'
2754281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
2764281ad8eSSimon Glass        'this=OBLIQUE_setfi2ndrstnd'
2774281ad8eSSimon Glass        """
278f60c9d4fSSimon Glass        re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
2794281ad8eSSimon Glass
2804281ad8eSSimon Glass        while True:
2814281ad8eSSimon Glass            m = re_var.search(args)
2824281ad8eSSimon Glass            if not m:
2834281ad8eSSimon Glass                break
2844281ad8eSSimon Glass            lookup = m.group(0)[2:-1]
2854281ad8eSSimon Glass            value = var_dict.get(lookup, '')
2864281ad8eSSimon Glass            args = args[:m.start(0)] + value + args[m.end(0):]
2874281ad8eSSimon Glass        return args
2884281ad8eSSimon Glass
2894281ad8eSSimon Glass    def GetMakeArguments(self, board):
2904281ad8eSSimon Glass        """Returns 'make' arguments for a given board
2914281ad8eSSimon Glass
2924281ad8eSSimon Glass        The flags are in a section called 'make-flags'. Flags are named
2934281ad8eSSimon Glass        after the target they represent, for example snapper9260=TESTING=1
2944281ad8eSSimon Glass        will pass TESTING=1 to make when building the snapper9260 board.
2954281ad8eSSimon Glass
2964281ad8eSSimon Glass        References to other boards can be added in the string also. For
2974281ad8eSSimon Glass        example:
2984281ad8eSSimon Glass
2994281ad8eSSimon Glass        [make-flags]
3004281ad8eSSimon Glass        at91-boards=ENABLE_AT91_TEST=1
3014281ad8eSSimon Glass        snapper9260=${at91-boards} BUILD_TAG=442
3024281ad8eSSimon Glass        snapper9g45=${at91-boards} BUILD_TAG=443
3034281ad8eSSimon Glass
3044281ad8eSSimon Glass        This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
3054281ad8eSSimon Glass        and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
3064281ad8eSSimon Glass
3074281ad8eSSimon Glass        A special 'target' variable is set to the board target.
3084281ad8eSSimon Glass
3094281ad8eSSimon Glass        Args:
3104281ad8eSSimon Glass            board: Board object for the board to check.
3114281ad8eSSimon Glass        Returns:
3124281ad8eSSimon Glass            'make' flags for that board, or '' if none
3134281ad8eSSimon Glass        """
3144281ad8eSSimon Glass        self._make_flags['target'] = board.target
3154281ad8eSSimon Glass        arg_str = self.ResolveReferences(self._make_flags,
3164281ad8eSSimon Glass                           self._make_flags.get(board.target, ''))
3174281ad8eSSimon Glass        args = arg_str.split(' ')
3184281ad8eSSimon Glass        i = 0
3194281ad8eSSimon Glass        while i < len(args):
3204281ad8eSSimon Glass            if not args[i]:
3214281ad8eSSimon Glass                del args[i]
3224281ad8eSSimon Glass            else:
3234281ad8eSSimon Glass                i += 1
3244281ad8eSSimon Glass        return args
325827e37b5SSimon Glass
326827e37b5SSimon Glass    def LocateArchUrl(self, fetch_arch):
327827e37b5SSimon Glass        """Find a toolchain available online
328827e37b5SSimon Glass
329827e37b5SSimon Glass        Look in standard places for available toolchains. At present the
330827e37b5SSimon Glass        only standard place is at kernel.org.
331827e37b5SSimon Glass
332827e37b5SSimon Glass        Args:
333827e37b5SSimon Glass            arch: Architecture to look for, or 'list' for all
334827e37b5SSimon Glass        Returns:
335827e37b5SSimon Glass            If fetch_arch is 'list', a tuple:
336827e37b5SSimon Glass                Machine architecture (e.g. x86_64)
337827e37b5SSimon Glass                List of toolchains
338827e37b5SSimon Glass            else
339827e37b5SSimon Glass                URL containing this toolchain, if avaialble, else None
340827e37b5SSimon Glass        """
341827e37b5SSimon Glass        arch = command.OutputOneLine('uname', '-m')
342827e37b5SSimon Glass        base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
3431246231cSMichal Simek        versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4']
344827e37b5SSimon Glass        links = []
345827e37b5SSimon Glass        for version in versions:
346827e37b5SSimon Glass            url = '%s/%s/%s/' % (base, arch, version)
347827e37b5SSimon Glass            print 'Checking: %s' % url
348827e37b5SSimon Glass            response = urllib2.urlopen(url)
349827e37b5SSimon Glass            html = response.read()
350827e37b5SSimon Glass            parser = MyHTMLParser(fetch_arch)
351827e37b5SSimon Glass            parser.feed(html)
352827e37b5SSimon Glass            if fetch_arch == 'list':
353827e37b5SSimon Glass                links += parser.links
354827e37b5SSimon Glass            elif parser.arch_link:
355827e37b5SSimon Glass                return url + parser.arch_link
356827e37b5SSimon Glass        if fetch_arch == 'list':
357827e37b5SSimon Glass            return arch, links
358827e37b5SSimon Glass        return None
359827e37b5SSimon Glass
360827e37b5SSimon Glass    def Download(self, url):
361827e37b5SSimon Glass        """Download a file to a temporary directory
362827e37b5SSimon Glass
363827e37b5SSimon Glass        Args:
364827e37b5SSimon Glass            url: URL to download
365827e37b5SSimon Glass        Returns:
366827e37b5SSimon Glass            Tuple:
367827e37b5SSimon Glass                Temporary directory name
368827e37b5SSimon Glass                Full path to the downloaded archive file in that directory,
369827e37b5SSimon Glass                    or None if there was an error while downloading
370827e37b5SSimon Glass        """
371*ad24ebacSSimon Glass        print 'Downloading: %s' % url
372827e37b5SSimon Glass        leaf = url.split('/')[-1]
373827e37b5SSimon Glass        tmpdir = tempfile.mkdtemp('.buildman')
374827e37b5SSimon Glass        response = urllib2.urlopen(url)
375827e37b5SSimon Glass        fname = os.path.join(tmpdir, leaf)
376827e37b5SSimon Glass        fd = open(fname, 'wb')
377827e37b5SSimon Glass        meta = response.info()
378*ad24ebacSSimon Glass        size = int(meta.getheaders('Content-Length')[0])
379827e37b5SSimon Glass        done = 0
380827e37b5SSimon Glass        block_size = 1 << 16
381827e37b5SSimon Glass        status = ''
382827e37b5SSimon Glass
383827e37b5SSimon Glass        # Read the file in chunks and show progress as we go
384827e37b5SSimon Glass        while True:
385827e37b5SSimon Glass            buffer = response.read(block_size)
386827e37b5SSimon Glass            if not buffer:
387827e37b5SSimon Glass                print chr(8) * (len(status) + 1), '\r',
388827e37b5SSimon Glass                break
389827e37b5SSimon Glass
390827e37b5SSimon Glass            done += len(buffer)
391827e37b5SSimon Glass            fd.write(buffer)
392*ad24ebacSSimon Glass            status = r'%10d MiB  [%3d%%]' % (done / 1024 / 1024,
393827e37b5SSimon Glass                                             done * 100 / size)
394827e37b5SSimon Glass            status = status + chr(8) * (len(status) + 1)
395827e37b5SSimon Glass            print status,
396827e37b5SSimon Glass            sys.stdout.flush()
397827e37b5SSimon Glass        fd.close()
398827e37b5SSimon Glass        if done != size:
399827e37b5SSimon Glass            print 'Error, failed to download'
400827e37b5SSimon Glass            os.remove(fname)
401827e37b5SSimon Glass            fname = None
402827e37b5SSimon Glass        return tmpdir, fname
403827e37b5SSimon Glass
404827e37b5SSimon Glass    def Unpack(self, fname, dest):
405827e37b5SSimon Glass        """Unpack a tar file
406827e37b5SSimon Glass
407827e37b5SSimon Glass        Args:
408827e37b5SSimon Glass            fname: Filename to unpack
409827e37b5SSimon Glass            dest: Destination directory
410827e37b5SSimon Glass        Returns:
411827e37b5SSimon Glass            Directory name of the first entry in the archive, without the
412827e37b5SSimon Glass            trailing /
413827e37b5SSimon Glass        """
414827e37b5SSimon Glass        stdout = command.Output('tar', 'xvfJ', fname, '-C', dest)
415827e37b5SSimon Glass        return stdout.splitlines()[0][:-1]
416827e37b5SSimon Glass
417827e37b5SSimon Glass    def TestSettingsHasPath(self, path):
418827e37b5SSimon Glass        """Check if builmand will find this toolchain
419827e37b5SSimon Glass
420827e37b5SSimon Glass        Returns:
421827e37b5SSimon Glass            True if the path is in settings, False if not
422827e37b5SSimon Glass        """
423827e37b5SSimon Glass        paths = self.GetPathList()
424827e37b5SSimon Glass        return path in paths
425827e37b5SSimon Glass
426827e37b5SSimon Glass    def ListArchs(self):
427827e37b5SSimon Glass        """List architectures with available toolchains to download"""
428827e37b5SSimon Glass        host_arch, archives = self.LocateArchUrl('list')
429827e37b5SSimon Glass        re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*')
430827e37b5SSimon Glass        arch_set = set()
431827e37b5SSimon Glass        for archive in archives:
432827e37b5SSimon Glass            # Remove the host architecture from the start
433827e37b5SSimon Glass            arch = re_arch.match(archive[len(host_arch):])
434827e37b5SSimon Glass            if arch:
435827e37b5SSimon Glass                arch_set.add(arch.group(1))
436827e37b5SSimon Glass        return sorted(arch_set)
437827e37b5SSimon Glass
438827e37b5SSimon Glass    def FetchAndInstall(self, arch):
439827e37b5SSimon Glass        """Fetch and install a new toolchain
440827e37b5SSimon Glass
441827e37b5SSimon Glass        arch:
442827e37b5SSimon Glass            Architecture to fetch, or 'list' to list
443827e37b5SSimon Glass        """
444827e37b5SSimon Glass        # Fist get the URL for this architecture
445827e37b5SSimon Glass        url = self.LocateArchUrl(arch)
446827e37b5SSimon Glass        if not url:
447827e37b5SSimon Glass            print ("Cannot find toolchain for arch '%s' - use 'list' to list" %
448827e37b5SSimon Glass                   arch)
449827e37b5SSimon Glass            return 2
450827e37b5SSimon Glass        home = os.environ['HOME']
451827e37b5SSimon Glass        dest = os.path.join(home, '.buildman-toolchains')
452827e37b5SSimon Glass        if not os.path.exists(dest):
453827e37b5SSimon Glass            os.mkdir(dest)
454827e37b5SSimon Glass
455827e37b5SSimon Glass        # Download the tar file for this toolchain and unpack it
456827e37b5SSimon Glass        tmpdir, tarfile = self.Download(url)
457827e37b5SSimon Glass        if not tarfile:
458827e37b5SSimon Glass            return 1
459827e37b5SSimon Glass        print 'Unpacking to: %s' % dest,
460827e37b5SSimon Glass        sys.stdout.flush()
461827e37b5SSimon Glass        path = self.Unpack(tarfile, dest)
462827e37b5SSimon Glass        os.remove(tarfile)
463827e37b5SSimon Glass        os.rmdir(tmpdir)
464827e37b5SSimon Glass        print
465827e37b5SSimon Glass
466827e37b5SSimon Glass        # Check that the toolchain works
467827e37b5SSimon Glass        print 'Testing'
468827e37b5SSimon Glass        dirpath = os.path.join(dest, path)
4692a76a649SSimon Glass        compiler_fname_list = self.ScanPath(dirpath, True)
4702a76a649SSimon Glass        if not compiler_fname_list:
471827e37b5SSimon Glass            print 'Could not locate C compiler - fetch failed.'
472827e37b5SSimon Glass            return 1
4732a76a649SSimon Glass        if len(compiler_fname_list) != 1:
4742a76a649SSimon Glass            print ('Internal error, ambiguous toolchains: %s' %
4752a76a649SSimon Glass                   (', '.join(compiler_fname)))
4762a76a649SSimon Glass            return 1
4772a76a649SSimon Glass        toolchain = Toolchain(compiler_fname_list[0], True, True)
478827e37b5SSimon Glass
479827e37b5SSimon Glass        # Make sure that it will be found by buildman
480827e37b5SSimon Glass        if not self.TestSettingsHasPath(dirpath):
481827e37b5SSimon Glass            print ("Adding 'download' to config file '%s'" %
482827e37b5SSimon Glass                   bsettings.config_fname)
483827e37b5SSimon Glass            tools_dir = os.path.dirname(dirpath)
484827e37b5SSimon Glass            bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir)
485827e37b5SSimon Glass        return 0
486