xref: /rk3399_rockchip-uboot/tools/buildman/toolchain.py (revision 608e399fdef82e983db44c5cb8f5e772bba870e2)
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
17ff690df9SSimon GlassPRIORITY_CALC = 0
18ff690df9SSimon Glass
19827e37b5SSimon Glass# Simple class to collect links from a page
20827e37b5SSimon Glassclass MyHTMLParser(HTMLParser):
21827e37b5SSimon Glass    def __init__(self, arch):
22827e37b5SSimon Glass        """Create a new parser
23827e37b5SSimon Glass
24827e37b5SSimon Glass        After the parser runs, self.links will be set to a list of the links
25827e37b5SSimon Glass        to .xz archives found in the page, and self.arch_link will be set to
26827e37b5SSimon Glass        the one for the given architecture (or None if not found).
27827e37b5SSimon Glass
28827e37b5SSimon Glass        Args:
29827e37b5SSimon Glass            arch: Architecture to search for
30827e37b5SSimon Glass        """
31827e37b5SSimon Glass        HTMLParser.__init__(self)
32827e37b5SSimon Glass        self.arch_link = None
33827e37b5SSimon Glass        self.links = []
34827e37b5SSimon Glass        self._match = '_%s-' % arch
35827e37b5SSimon Glass
36827e37b5SSimon Glass    def handle_starttag(self, tag, attrs):
37827e37b5SSimon Glass        if tag == 'a':
38827e37b5SSimon Glass            for tag, value in attrs:
39827e37b5SSimon Glass                if tag == 'href':
40827e37b5SSimon Glass                    if value and value.endswith('.xz'):
41827e37b5SSimon Glass                        self.links.append(value)
42827e37b5SSimon Glass                        if self._match in value:
43827e37b5SSimon Glass                            self.arch_link = value
44827e37b5SSimon Glass
45827e37b5SSimon Glass
46fc3fe1c2SSimon Glassclass Toolchain:
47fc3fe1c2SSimon Glass    """A single toolchain
48fc3fe1c2SSimon Glass
49fc3fe1c2SSimon Glass    Public members:
50fc3fe1c2SSimon Glass        gcc: Full path to C compiler
51fc3fe1c2SSimon Glass        path: Directory path containing C compiler
52fc3fe1c2SSimon Glass        cross: Cross compile string, e.g. 'arm-linux-'
53fc3fe1c2SSimon Glass        arch: Architecture of toolchain as determined from the first
54fc3fe1c2SSimon Glass                component of the filename. E.g. arm-linux-gcc becomes arm
55ff690df9SSimon Glass        priority: Toolchain priority (0=highest, 20=lowest)
56fc3fe1c2SSimon Glass    """
57*608e399fSSimon Glass    def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC,
58*608e399fSSimon Glass                 arch=None):
59fc3fe1c2SSimon Glass        """Create a new toolchain object.
60fc3fe1c2SSimon Glass
61fc3fe1c2SSimon Glass        Args:
62fc3fe1c2SSimon Glass            fname: Filename of the gcc component
63fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
64ad24ebacSSimon Glass            verbose: True to print out the information
65ff690df9SSimon Glass            priority: Priority to use for this toolchain, or PRIORITY_CALC to
66ff690df9SSimon Glass                calculate it
67fc3fe1c2SSimon Glass        """
68fc3fe1c2SSimon Glass        self.gcc = fname
69fc3fe1c2SSimon Glass        self.path = os.path.dirname(fname)
70b5324123SSimon Glass
71b5324123SSimon Glass        # Find the CROSS_COMPILE prefix to use for U-Boot. For example,
72b5324123SSimon Glass        # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'.
73b5324123SSimon Glass        basename = os.path.basename(fname)
74b5324123SSimon Glass        pos = basename.rfind('-')
75b5324123SSimon Glass        self.cross = basename[:pos + 1] if pos != -1 else ''
76b5324123SSimon Glass
77b5324123SSimon Glass        # The architecture is the first part of the name
78fc3fe1c2SSimon Glass        pos = self.cross.find('-')
79*608e399fSSimon Glass        if arch:
80*608e399fSSimon Glass            self.arch = arch
81*608e399fSSimon Glass        else:
82fc3fe1c2SSimon Glass            self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
83fc3fe1c2SSimon Glass
84bb1501f2SSimon Glass        env = self.MakeEnvironment(False)
85fc3fe1c2SSimon Glass
86fc3fe1c2SSimon Glass        # As a basic sanity check, run the C compiler with --version
87fc3fe1c2SSimon Glass        cmd = [fname, '--version']
88ff690df9SSimon Glass        if priority == PRIORITY_CALC:
89ff690df9SSimon Glass            self.priority = self.GetPriority(fname)
90ff690df9SSimon Glass        else:
91ff690df9SSimon Glass            self.priority = priority
92fc3fe1c2SSimon Glass        if test:
938bb2bddcSStephen Warren            result = command.RunPipe([cmd], capture=True, env=env,
948bb2bddcSStephen Warren                                     raise_on_error=False)
95fc3fe1c2SSimon Glass            self.ok = result.return_code == 0
96fc3fe1c2SSimon Glass            if verbose:
97fc3fe1c2SSimon Glass                print 'Tool chain test: ',
98fc3fe1c2SSimon Glass                if self.ok:
99*608e399fSSimon Glass                    print "OK, arch='%s', priority %d" % (self.arch,
100*608e399fSSimon Glass                                                          self.priority)
101fc3fe1c2SSimon Glass                else:
102fc3fe1c2SSimon Glass                    print 'BAD'
103fc3fe1c2SSimon Glass                    print 'Command: ', cmd
104fc3fe1c2SSimon Glass                    print result.stdout
105fc3fe1c2SSimon Glass                    print result.stderr
106fc3fe1c2SSimon Glass        else:
107fc3fe1c2SSimon Glass            self.ok = True
108fc3fe1c2SSimon Glass
109fc3fe1c2SSimon Glass    def GetPriority(self, fname):
110fc3fe1c2SSimon Glass        """Return the priority of the toolchain.
111fc3fe1c2SSimon Glass
112fc3fe1c2SSimon Glass        Toolchains are ranked according to their suitability by their
113fc3fe1c2SSimon Glass        filename prefix.
114fc3fe1c2SSimon Glass
115fc3fe1c2SSimon Glass        Args:
116fc3fe1c2SSimon Glass            fname: Filename of toolchain
117fc3fe1c2SSimon Glass        Returns:
118ff690df9SSimon Glass            Priority of toolchain, PRIORITY_CALC=highest, 20=lowest.
119fc3fe1c2SSimon Glass        """
1208708267fSMasahiro Yamada        priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
121fc3fe1c2SSimon Glass            '-none-linux-gnueabi', '-uclinux', '-none-eabi',
122fc3fe1c2SSimon Glass            '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
123fc3fe1c2SSimon Glass        for prio in range(len(priority_list)):
124fc3fe1c2SSimon Glass            if priority_list[prio] in fname:
125ff690df9SSimon Glass                return PRIORITY_CALC + prio
126ff690df9SSimon Glass        return PRIORITY_CALC + prio
127fc3fe1c2SSimon Glass
128bb1501f2SSimon Glass    def MakeEnvironment(self, full_path):
129fc3fe1c2SSimon Glass        """Returns an environment for using the toolchain.
130fc3fe1c2SSimon Glass
131bb1501f2SSimon Glass        Thie takes the current environment and adds CROSS_COMPILE so that
132bb1501f2SSimon Glass        the tool chain will operate correctly.
133bb1501f2SSimon Glass
134bb1501f2SSimon Glass        Args:
135bb1501f2SSimon Glass            full_path: Return the full path in CROSS_COMPILE and don't set
136bb1501f2SSimon Glass                PATH
137fc3fe1c2SSimon Glass        """
138fc3fe1c2SSimon Glass        env = dict(os.environ)
139bb1501f2SSimon Glass        if full_path:
140bb1501f2SSimon Glass            env['CROSS_COMPILE'] = os.path.join(self.path, self.cross)
141bb1501f2SSimon Glass        else:
142fc3fe1c2SSimon Glass            env['CROSS_COMPILE'] = self.cross
143f210b587SSimon Glass            env['PATH'] = self.path + ':' + env['PATH']
144bb1501f2SSimon Glass
145fc3fe1c2SSimon Glass        return env
146fc3fe1c2SSimon Glass
147fc3fe1c2SSimon Glass
148fc3fe1c2SSimon Glassclass Toolchains:
149fc3fe1c2SSimon Glass    """Manage a list of toolchains for building U-Boot
150fc3fe1c2SSimon Glass
151fc3fe1c2SSimon Glass    We select one toolchain for each architecture type
152fc3fe1c2SSimon Glass
153fc3fe1c2SSimon Glass    Public members:
154fc3fe1c2SSimon Glass        toolchains: Dict of Toolchain objects, keyed by architecture name
155fc3fe1c2SSimon Glass        paths: List of paths to check for toolchains (may contain wildcards)
156fc3fe1c2SSimon Glass    """
157fc3fe1c2SSimon Glass
158fc3fe1c2SSimon Glass    def __init__(self):
159fc3fe1c2SSimon Glass        self.toolchains = {}
160fc3fe1c2SSimon Glass        self.paths = []
161d4144e45SSimon Glass        self._make_flags = dict(bsettings.GetItems('make-flags'))
162d4144e45SSimon Glass
163827e37b5SSimon Glass    def GetPathList(self):
164827e37b5SSimon Glass        """Get a list of available toolchain paths
165827e37b5SSimon Glass
166827e37b5SSimon Glass        Returns:
167827e37b5SSimon Glass            List of strings, each a path to a toolchain mentioned in the
168827e37b5SSimon Glass            [toolchain] section of the settings file.
169827e37b5SSimon Glass        """
1704281ad8eSSimon Glass        toolchains = bsettings.GetItems('toolchain')
1714281ad8eSSimon Glass        if not toolchains:
172ad24ebacSSimon Glass            print ('Warning: No tool chains - please add a [toolchain] section'
173ad24ebacSSimon Glass                 ' to your buildman config file %s. See README for details' %
1741826a18dSMasahiro Yamada                 bsettings.config_fname)
1754281ad8eSSimon Glass
176827e37b5SSimon Glass        paths = []
1774281ad8eSSimon Glass        for name, value in toolchains:
178fc3fe1c2SSimon Glass            if '*' in value:
179827e37b5SSimon Glass                paths += glob.glob(value)
180fc3fe1c2SSimon Glass            else:
181827e37b5SSimon Glass                paths.append(value)
182827e37b5SSimon Glass        return paths
183827e37b5SSimon Glass
184827e37b5SSimon Glass    def GetSettings(self):
185827e37b5SSimon Glass      self.paths += self.GetPathList()
186fc3fe1c2SSimon Glass
187*608e399fSSimon Glass    def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
188*608e399fSSimon Glass            arch=None):
189fc3fe1c2SSimon Glass        """Add a toolchain to our list
190fc3fe1c2SSimon Glass
191fc3fe1c2SSimon Glass        We select the given toolchain as our preferred one for its
192fc3fe1c2SSimon Glass        architecture if it is a higher priority than the others.
193fc3fe1c2SSimon Glass
194fc3fe1c2SSimon Glass        Args:
195fc3fe1c2SSimon Glass            fname: Filename of toolchain's gcc driver
196fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
197ff690df9SSimon Glass            priority: Priority to use for this toolchain
198*608e399fSSimon Glass            arch: Toolchain architecture, or None if not known
199fc3fe1c2SSimon Glass        """
200*608e399fSSimon Glass        toolchain = Toolchain(fname, test, verbose, priority, arch)
201fc3fe1c2SSimon Glass        add_it = toolchain.ok
202fc3fe1c2SSimon Glass        if toolchain.arch in self.toolchains:
203fc3fe1c2SSimon Glass            add_it = (toolchain.priority <
204fc3fe1c2SSimon Glass                        self.toolchains[toolchain.arch].priority)
205fc3fe1c2SSimon Glass        if add_it:
206fc3fe1c2SSimon Glass            self.toolchains[toolchain.arch] = toolchain
207ff690df9SSimon Glass        elif verbose:
208ff690df9SSimon Glass            print ("Toolchain '%s' at priority %d will be ignored because "
209ff690df9SSimon Glass                   "another toolchain for arch '%s' has priority %d" %
210ff690df9SSimon Glass                   (toolchain.gcc, toolchain.priority, toolchain.arch,
211ff690df9SSimon Glass                    self.toolchains[toolchain.arch].priority))
212fc3fe1c2SSimon Glass
213827e37b5SSimon Glass    def ScanPath(self, path, verbose):
214827e37b5SSimon Glass        """Scan a path for a valid toolchain
215827e37b5SSimon Glass
216827e37b5SSimon Glass        Args:
217827e37b5SSimon Glass            path: Path to scan
218827e37b5SSimon Glass            verbose: True to print out progress information
219827e37b5SSimon Glass        Returns:
220827e37b5SSimon Glass            Filename of C compiler if found, else None
221827e37b5SSimon Glass        """
222d9088983SAlbert ARIBAUD        fnames = []
223827e37b5SSimon Glass        for subdir in ['.', 'bin', 'usr/bin']:
224827e37b5SSimon Glass            dirname = os.path.join(path, subdir)
225827e37b5SSimon Glass            if verbose: print "      - looking in '%s'" % dirname
226827e37b5SSimon Glass            for fname in glob.glob(dirname + '/*gcc'):
227827e37b5SSimon Glass                if verbose: print "         - found '%s'" % fname
228d9088983SAlbert ARIBAUD                fnames.append(fname)
229d9088983SAlbert ARIBAUD        return fnames
230827e37b5SSimon Glass
231827e37b5SSimon Glass
232fc3fe1c2SSimon Glass    def Scan(self, verbose):
233fc3fe1c2SSimon Glass        """Scan for available toolchains and select the best for each arch.
234fc3fe1c2SSimon Glass
235fc3fe1c2SSimon Glass        We look for all the toolchains we can file, figure out the
236fc3fe1c2SSimon Glass        architecture for each, and whether it works. Then we select the
237fc3fe1c2SSimon Glass        highest priority toolchain for each arch.
238fc3fe1c2SSimon Glass
239fc3fe1c2SSimon Glass        Args:
240fc3fe1c2SSimon Glass            verbose: True to print out progress information
241fc3fe1c2SSimon Glass        """
242fc3fe1c2SSimon Glass        if verbose: print 'Scanning for tool chains'
243fc3fe1c2SSimon Glass        for path in self.paths:
244fc3fe1c2SSimon Glass            if verbose: print "   - scanning path '%s'" % path
245d9088983SAlbert ARIBAUD            fnames = self.ScanPath(path, verbose)
246d9088983SAlbert ARIBAUD            for fname in fnames:
247fc3fe1c2SSimon Glass                self.Add(fname, True, verbose)
248fc3fe1c2SSimon Glass
249fc3fe1c2SSimon Glass    def List(self):
250fc3fe1c2SSimon Glass        """List out the selected toolchains for each architecture"""
251fc3fe1c2SSimon Glass        print 'List of available toolchains (%d):' % len(self.toolchains)
252fc3fe1c2SSimon Glass        if len(self.toolchains):
253fc3fe1c2SSimon Glass            for key, value in sorted(self.toolchains.iteritems()):
254fc3fe1c2SSimon Glass                print '%-10s: %s' % (key, value.gcc)
255fc3fe1c2SSimon Glass        else:
256fc3fe1c2SSimon Glass            print 'None'
257fc3fe1c2SSimon Glass
258fc3fe1c2SSimon Glass    def Select(self, arch):
259fc3fe1c2SSimon Glass        """Returns the toolchain for a given architecture
260fc3fe1c2SSimon Glass
261fc3fe1c2SSimon Glass        Args:
262fc3fe1c2SSimon Glass            args: Name of architecture (e.g. 'arm', 'ppc_8xx')
263fc3fe1c2SSimon Glass
264fc3fe1c2SSimon Glass        returns:
265fc3fe1c2SSimon Glass            toolchain object, or None if none found
266fc3fe1c2SSimon Glass        """
2679b83bfdcSSimon Glass        for tag, value in bsettings.GetItems('toolchain-alias'):
2689b83bfdcSSimon Glass            if arch == tag:
2699b83bfdcSSimon Glass                for alias in value.split():
2709b83bfdcSSimon Glass                    if alias in self.toolchains:
2719b83bfdcSSimon Glass                        return self.toolchains[alias]
272fc3fe1c2SSimon Glass
273fc3fe1c2SSimon Glass        if not arch in self.toolchains:
274fc3fe1c2SSimon Glass            raise ValueError, ("No tool chain found for arch '%s'" % arch)
275fc3fe1c2SSimon Glass        return self.toolchains[arch]
2764281ad8eSSimon Glass
2774281ad8eSSimon Glass    def ResolveReferences(self, var_dict, args):
2784281ad8eSSimon Glass        """Resolve variable references in a string
2794281ad8eSSimon Glass
2804281ad8eSSimon Glass        This converts ${blah} within the string to the value of blah.
2814281ad8eSSimon Glass        This function works recursively.
2824281ad8eSSimon Glass
2834281ad8eSSimon Glass        Args:
2844281ad8eSSimon Glass            var_dict: Dictionary containing variables and their values
2854281ad8eSSimon Glass            args: String containing make arguments
2864281ad8eSSimon Glass        Returns:
2874281ad8eSSimon Glass            Resolved string
2884281ad8eSSimon Glass
2894281ad8eSSimon Glass        >>> bsettings.Setup()
2904281ad8eSSimon Glass        >>> tcs = Toolchains()
2914281ad8eSSimon Glass        >>> tcs.Add('fred', False)
2924281ad8eSSimon Glass        >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
2934281ad8eSSimon Glass                        'second' : '2nd'}
2944281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
2954281ad8eSSimon Glass        'this=OBLIQUE_set'
2964281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
2974281ad8eSSimon Glass        'this=OBLIQUE_setfi2ndrstnd'
2984281ad8eSSimon Glass        """
299f60c9d4fSSimon Glass        re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
3004281ad8eSSimon Glass
3014281ad8eSSimon Glass        while True:
3024281ad8eSSimon Glass            m = re_var.search(args)
3034281ad8eSSimon Glass            if not m:
3044281ad8eSSimon Glass                break
3054281ad8eSSimon Glass            lookup = m.group(0)[2:-1]
3064281ad8eSSimon Glass            value = var_dict.get(lookup, '')
3074281ad8eSSimon Glass            args = args[:m.start(0)] + value + args[m.end(0):]
3084281ad8eSSimon Glass        return args
3094281ad8eSSimon Glass
3104281ad8eSSimon Glass    def GetMakeArguments(self, board):
3114281ad8eSSimon Glass        """Returns 'make' arguments for a given board
3124281ad8eSSimon Glass
3134281ad8eSSimon Glass        The flags are in a section called 'make-flags'. Flags are named
3144281ad8eSSimon Glass        after the target they represent, for example snapper9260=TESTING=1
3154281ad8eSSimon Glass        will pass TESTING=1 to make when building the snapper9260 board.
3164281ad8eSSimon Glass
3174281ad8eSSimon Glass        References to other boards can be added in the string also. For
3184281ad8eSSimon Glass        example:
3194281ad8eSSimon Glass
3204281ad8eSSimon Glass        [make-flags]
3214281ad8eSSimon Glass        at91-boards=ENABLE_AT91_TEST=1
3224281ad8eSSimon Glass        snapper9260=${at91-boards} BUILD_TAG=442
3234281ad8eSSimon Glass        snapper9g45=${at91-boards} BUILD_TAG=443
3244281ad8eSSimon Glass
3254281ad8eSSimon Glass        This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
3264281ad8eSSimon Glass        and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
3274281ad8eSSimon Glass
3284281ad8eSSimon Glass        A special 'target' variable is set to the board target.
3294281ad8eSSimon Glass
3304281ad8eSSimon Glass        Args:
3314281ad8eSSimon Glass            board: Board object for the board to check.
3324281ad8eSSimon Glass        Returns:
3334281ad8eSSimon Glass            'make' flags for that board, or '' if none
3344281ad8eSSimon Glass        """
3354281ad8eSSimon Glass        self._make_flags['target'] = board.target
3364281ad8eSSimon Glass        arg_str = self.ResolveReferences(self._make_flags,
3374281ad8eSSimon Glass                           self._make_flags.get(board.target, ''))
3384281ad8eSSimon Glass        args = arg_str.split(' ')
3394281ad8eSSimon Glass        i = 0
3404281ad8eSSimon Glass        while i < len(args):
3414281ad8eSSimon Glass            if not args[i]:
3424281ad8eSSimon Glass                del args[i]
3434281ad8eSSimon Glass            else:
3444281ad8eSSimon Glass                i += 1
3454281ad8eSSimon Glass        return args
346827e37b5SSimon Glass
347827e37b5SSimon Glass    def LocateArchUrl(self, fetch_arch):
348827e37b5SSimon Glass        """Find a toolchain available online
349827e37b5SSimon Glass
350827e37b5SSimon Glass        Look in standard places for available toolchains. At present the
351827e37b5SSimon Glass        only standard place is at kernel.org.
352827e37b5SSimon Glass
353827e37b5SSimon Glass        Args:
354827e37b5SSimon Glass            arch: Architecture to look for, or 'list' for all
355827e37b5SSimon Glass        Returns:
356827e37b5SSimon Glass            If fetch_arch is 'list', a tuple:
357827e37b5SSimon Glass                Machine architecture (e.g. x86_64)
358827e37b5SSimon Glass                List of toolchains
359827e37b5SSimon Glass            else
360827e37b5SSimon Glass                URL containing this toolchain, if avaialble, else None
361827e37b5SSimon Glass        """
362827e37b5SSimon Glass        arch = command.OutputOneLine('uname', '-m')
363827e37b5SSimon Glass        base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
3641246231cSMichal Simek        versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4']
365827e37b5SSimon Glass        links = []
366827e37b5SSimon Glass        for version in versions:
367827e37b5SSimon Glass            url = '%s/%s/%s/' % (base, arch, version)
368827e37b5SSimon Glass            print 'Checking: %s' % url
369827e37b5SSimon Glass            response = urllib2.urlopen(url)
370827e37b5SSimon Glass            html = response.read()
371827e37b5SSimon Glass            parser = MyHTMLParser(fetch_arch)
372827e37b5SSimon Glass            parser.feed(html)
373827e37b5SSimon Glass            if fetch_arch == 'list':
374827e37b5SSimon Glass                links += parser.links
375827e37b5SSimon Glass            elif parser.arch_link:
376827e37b5SSimon Glass                return url + parser.arch_link
377827e37b5SSimon Glass        if fetch_arch == 'list':
378827e37b5SSimon Glass            return arch, links
379827e37b5SSimon Glass        return None
380827e37b5SSimon Glass
381827e37b5SSimon Glass    def Download(self, url):
382827e37b5SSimon Glass        """Download a file to a temporary directory
383827e37b5SSimon Glass
384827e37b5SSimon Glass        Args:
385827e37b5SSimon Glass            url: URL to download
386827e37b5SSimon Glass        Returns:
387827e37b5SSimon Glass            Tuple:
388827e37b5SSimon Glass                Temporary directory name
389827e37b5SSimon Glass                Full path to the downloaded archive file in that directory,
390827e37b5SSimon Glass                    or None if there was an error while downloading
391827e37b5SSimon Glass        """
392ad24ebacSSimon Glass        print 'Downloading: %s' % url
393827e37b5SSimon Glass        leaf = url.split('/')[-1]
394827e37b5SSimon Glass        tmpdir = tempfile.mkdtemp('.buildman')
395827e37b5SSimon Glass        response = urllib2.urlopen(url)
396827e37b5SSimon Glass        fname = os.path.join(tmpdir, leaf)
397827e37b5SSimon Glass        fd = open(fname, 'wb')
398827e37b5SSimon Glass        meta = response.info()
399ad24ebacSSimon Glass        size = int(meta.getheaders('Content-Length')[0])
400827e37b5SSimon Glass        done = 0
401827e37b5SSimon Glass        block_size = 1 << 16
402827e37b5SSimon Glass        status = ''
403827e37b5SSimon Glass
404827e37b5SSimon Glass        # Read the file in chunks and show progress as we go
405827e37b5SSimon Glass        while True:
406827e37b5SSimon Glass            buffer = response.read(block_size)
407827e37b5SSimon Glass            if not buffer:
408827e37b5SSimon Glass                print chr(8) * (len(status) + 1), '\r',
409827e37b5SSimon Glass                break
410827e37b5SSimon Glass
411827e37b5SSimon Glass            done += len(buffer)
412827e37b5SSimon Glass            fd.write(buffer)
413ad24ebacSSimon Glass            status = r'%10d MiB  [%3d%%]' % (done / 1024 / 1024,
414827e37b5SSimon Glass                                             done * 100 / size)
415827e37b5SSimon Glass            status = status + chr(8) * (len(status) + 1)
416827e37b5SSimon Glass            print status,
417827e37b5SSimon Glass            sys.stdout.flush()
418827e37b5SSimon Glass        fd.close()
419827e37b5SSimon Glass        if done != size:
420827e37b5SSimon Glass            print 'Error, failed to download'
421827e37b5SSimon Glass            os.remove(fname)
422827e37b5SSimon Glass            fname = None
423827e37b5SSimon Glass        return tmpdir, fname
424827e37b5SSimon Glass
425827e37b5SSimon Glass    def Unpack(self, fname, dest):
426827e37b5SSimon Glass        """Unpack a tar file
427827e37b5SSimon Glass
428827e37b5SSimon Glass        Args:
429827e37b5SSimon Glass            fname: Filename to unpack
430827e37b5SSimon Glass            dest: Destination directory
431827e37b5SSimon Glass        Returns:
432827e37b5SSimon Glass            Directory name of the first entry in the archive, without the
433827e37b5SSimon Glass            trailing /
434827e37b5SSimon Glass        """
435827e37b5SSimon Glass        stdout = command.Output('tar', 'xvfJ', fname, '-C', dest)
436827e37b5SSimon Glass        return stdout.splitlines()[0][:-1]
437827e37b5SSimon Glass
438827e37b5SSimon Glass    def TestSettingsHasPath(self, path):
439827e37b5SSimon Glass        """Check if builmand will find this toolchain
440827e37b5SSimon Glass
441827e37b5SSimon Glass        Returns:
442827e37b5SSimon Glass            True if the path is in settings, False if not
443827e37b5SSimon Glass        """
444827e37b5SSimon Glass        paths = self.GetPathList()
445827e37b5SSimon Glass        return path in paths
446827e37b5SSimon Glass
447827e37b5SSimon Glass    def ListArchs(self):
448827e37b5SSimon Glass        """List architectures with available toolchains to download"""
449827e37b5SSimon Glass        host_arch, archives = self.LocateArchUrl('list')
450827e37b5SSimon Glass        re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*')
451827e37b5SSimon Glass        arch_set = set()
452827e37b5SSimon Glass        for archive in archives:
453827e37b5SSimon Glass            # Remove the host architecture from the start
454827e37b5SSimon Glass            arch = re_arch.match(archive[len(host_arch):])
455827e37b5SSimon Glass            if arch:
456827e37b5SSimon Glass                arch_set.add(arch.group(1))
457827e37b5SSimon Glass        return sorted(arch_set)
458827e37b5SSimon Glass
459827e37b5SSimon Glass    def FetchAndInstall(self, arch):
460827e37b5SSimon Glass        """Fetch and install a new toolchain
461827e37b5SSimon Glass
462827e37b5SSimon Glass        arch:
463827e37b5SSimon Glass            Architecture to fetch, or 'list' to list
464827e37b5SSimon Glass        """
465827e37b5SSimon Glass        # Fist get the URL for this architecture
466827e37b5SSimon Glass        url = self.LocateArchUrl(arch)
467827e37b5SSimon Glass        if not url:
468827e37b5SSimon Glass            print ("Cannot find toolchain for arch '%s' - use 'list' to list" %
469827e37b5SSimon Glass                   arch)
470827e37b5SSimon Glass            return 2
471827e37b5SSimon Glass        home = os.environ['HOME']
472827e37b5SSimon Glass        dest = os.path.join(home, '.buildman-toolchains')
473827e37b5SSimon Glass        if not os.path.exists(dest):
474827e37b5SSimon Glass            os.mkdir(dest)
475827e37b5SSimon Glass
476827e37b5SSimon Glass        # Download the tar file for this toolchain and unpack it
477827e37b5SSimon Glass        tmpdir, tarfile = self.Download(url)
478827e37b5SSimon Glass        if not tarfile:
479827e37b5SSimon Glass            return 1
480827e37b5SSimon Glass        print 'Unpacking to: %s' % dest,
481827e37b5SSimon Glass        sys.stdout.flush()
482827e37b5SSimon Glass        path = self.Unpack(tarfile, dest)
483827e37b5SSimon Glass        os.remove(tarfile)
484827e37b5SSimon Glass        os.rmdir(tmpdir)
485827e37b5SSimon Glass        print
486827e37b5SSimon Glass
487827e37b5SSimon Glass        # Check that the toolchain works
488827e37b5SSimon Glass        print 'Testing'
489827e37b5SSimon Glass        dirpath = os.path.join(dest, path)
4902a76a649SSimon Glass        compiler_fname_list = self.ScanPath(dirpath, True)
4912a76a649SSimon Glass        if not compiler_fname_list:
492827e37b5SSimon Glass            print 'Could not locate C compiler - fetch failed.'
493827e37b5SSimon Glass            return 1
4942a76a649SSimon Glass        if len(compiler_fname_list) != 1:
4952a76a649SSimon Glass            print ('Internal error, ambiguous toolchains: %s' %
4962a76a649SSimon Glass                   (', '.join(compiler_fname)))
4972a76a649SSimon Glass            return 1
4982a76a649SSimon Glass        toolchain = Toolchain(compiler_fname_list[0], True, True)
499827e37b5SSimon Glass
500827e37b5SSimon Glass        # Make sure that it will be found by buildman
501827e37b5SSimon Glass        if not self.TestSettingsHasPath(dirpath):
502827e37b5SSimon Glass            print ("Adding 'download' to config file '%s'" %
503827e37b5SSimon Glass                   bsettings.config_fname)
504827e37b5SSimon Glass            tools_dir = os.path.dirname(dirpath)
505827e37b5SSimon Glass            bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir)
506827e37b5SSimon Glass        return 0
507