xref: /rk3399_rockchip-uboot/tools/buildman/toolchain.py (revision 713bea38dde23794aa7ff6a276829b54a7ffbd99)
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
16*713bea38SSimon Glassimport terminal
17fc3fe1c2SSimon Glass
1817bce66cSSimon Glass(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH,
1917bce66cSSimon Glass    PRIORITY_CALC) = range(4)
20ff690df9SSimon Glass
21827e37b5SSimon Glass# Simple class to collect links from a page
22827e37b5SSimon Glassclass MyHTMLParser(HTMLParser):
23827e37b5SSimon Glass    def __init__(self, arch):
24827e37b5SSimon Glass        """Create a new parser
25827e37b5SSimon Glass
26827e37b5SSimon Glass        After the parser runs, self.links will be set to a list of the links
27827e37b5SSimon Glass        to .xz archives found in the page, and self.arch_link will be set to
28827e37b5SSimon Glass        the one for the given architecture (or None if not found).
29827e37b5SSimon Glass
30827e37b5SSimon Glass        Args:
31827e37b5SSimon Glass            arch: Architecture to search for
32827e37b5SSimon Glass        """
33827e37b5SSimon Glass        HTMLParser.__init__(self)
34827e37b5SSimon Glass        self.arch_link = None
35827e37b5SSimon Glass        self.links = []
36827e37b5SSimon Glass        self._match = '_%s-' % arch
37827e37b5SSimon Glass
38827e37b5SSimon Glass    def handle_starttag(self, tag, attrs):
39827e37b5SSimon Glass        if tag == 'a':
40827e37b5SSimon Glass            for tag, value in attrs:
41827e37b5SSimon Glass                if tag == 'href':
42827e37b5SSimon Glass                    if value and value.endswith('.xz'):
43827e37b5SSimon Glass                        self.links.append(value)
44827e37b5SSimon Glass                        if self._match in value:
45827e37b5SSimon Glass                            self.arch_link = value
46827e37b5SSimon Glass
47827e37b5SSimon Glass
48fc3fe1c2SSimon Glassclass Toolchain:
49fc3fe1c2SSimon Glass    """A single toolchain
50fc3fe1c2SSimon Glass
51fc3fe1c2SSimon Glass    Public members:
52fc3fe1c2SSimon Glass        gcc: Full path to C compiler
53fc3fe1c2SSimon Glass        path: Directory path containing C compiler
54fc3fe1c2SSimon Glass        cross: Cross compile string, e.g. 'arm-linux-'
55fc3fe1c2SSimon Glass        arch: Architecture of toolchain as determined from the first
56fc3fe1c2SSimon Glass                component of the filename. E.g. arm-linux-gcc becomes arm
57ff690df9SSimon Glass        priority: Toolchain priority (0=highest, 20=lowest)
58fc3fe1c2SSimon Glass    """
59608e399fSSimon Glass    def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC,
60608e399fSSimon Glass                 arch=None):
61fc3fe1c2SSimon Glass        """Create a new toolchain object.
62fc3fe1c2SSimon Glass
63fc3fe1c2SSimon Glass        Args:
64fc3fe1c2SSimon Glass            fname: Filename of the gcc component
65fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
66ad24ebacSSimon Glass            verbose: True to print out the information
67ff690df9SSimon Glass            priority: Priority to use for this toolchain, or PRIORITY_CALC to
68ff690df9SSimon Glass                calculate it
69fc3fe1c2SSimon Glass        """
70fc3fe1c2SSimon Glass        self.gcc = fname
71fc3fe1c2SSimon Glass        self.path = os.path.dirname(fname)
72b5324123SSimon Glass
73b5324123SSimon Glass        # Find the CROSS_COMPILE prefix to use for U-Boot. For example,
74b5324123SSimon Glass        # 'arm-linux-gnueabihf-gcc' turns into 'arm-linux-gnueabihf-'.
75b5324123SSimon Glass        basename = os.path.basename(fname)
76b5324123SSimon Glass        pos = basename.rfind('-')
77b5324123SSimon Glass        self.cross = basename[:pos + 1] if pos != -1 else ''
78b5324123SSimon Glass
79b5324123SSimon Glass        # The architecture is the first part of the name
80fc3fe1c2SSimon Glass        pos = self.cross.find('-')
81608e399fSSimon Glass        if arch:
82608e399fSSimon Glass            self.arch = arch
83608e399fSSimon Glass        else:
84fc3fe1c2SSimon Glass            self.arch = self.cross[:pos] if pos != -1 else 'sandbox'
85fc3fe1c2SSimon Glass
86bb1501f2SSimon Glass        env = self.MakeEnvironment(False)
87fc3fe1c2SSimon Glass
88fc3fe1c2SSimon Glass        # As a basic sanity check, run the C compiler with --version
89fc3fe1c2SSimon Glass        cmd = [fname, '--version']
90ff690df9SSimon Glass        if priority == PRIORITY_CALC:
91ff690df9SSimon Glass            self.priority = self.GetPriority(fname)
92ff690df9SSimon Glass        else:
93ff690df9SSimon Glass            self.priority = priority
94fc3fe1c2SSimon Glass        if test:
958bb2bddcSStephen Warren            result = command.RunPipe([cmd], capture=True, env=env,
968bb2bddcSStephen Warren                                     raise_on_error=False)
97fc3fe1c2SSimon Glass            self.ok = result.return_code == 0
98fc3fe1c2SSimon Glass            if verbose:
99fc3fe1c2SSimon Glass                print 'Tool chain test: ',
100fc3fe1c2SSimon Glass                if self.ok:
101608e399fSSimon Glass                    print "OK, arch='%s', priority %d" % (self.arch,
102608e399fSSimon Glass                                                          self.priority)
103fc3fe1c2SSimon Glass                else:
104fc3fe1c2SSimon Glass                    print 'BAD'
105fc3fe1c2SSimon Glass                    print 'Command: ', cmd
106fc3fe1c2SSimon Glass                    print result.stdout
107fc3fe1c2SSimon Glass                    print result.stderr
108fc3fe1c2SSimon Glass        else:
109fc3fe1c2SSimon Glass            self.ok = True
110fc3fe1c2SSimon Glass
111fc3fe1c2SSimon Glass    def GetPriority(self, fname):
112fc3fe1c2SSimon Glass        """Return the priority of the toolchain.
113fc3fe1c2SSimon Glass
114fc3fe1c2SSimon Glass        Toolchains are ranked according to their suitability by their
115fc3fe1c2SSimon Glass        filename prefix.
116fc3fe1c2SSimon Glass
117fc3fe1c2SSimon Glass        Args:
118fc3fe1c2SSimon Glass            fname: Filename of toolchain
119fc3fe1c2SSimon Glass        Returns:
120ff690df9SSimon Glass            Priority of toolchain, PRIORITY_CALC=highest, 20=lowest.
121fc3fe1c2SSimon Glass        """
1228708267fSMasahiro Yamada        priority_list = ['-elf', '-unknown-linux-gnu', '-linux',
123fc3fe1c2SSimon Glass            '-none-linux-gnueabi', '-uclinux', '-none-eabi',
124fc3fe1c2SSimon Glass            '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux']
125fc3fe1c2SSimon Glass        for prio in range(len(priority_list)):
126fc3fe1c2SSimon Glass            if priority_list[prio] in fname:
127ff690df9SSimon Glass                return PRIORITY_CALC + prio
128ff690df9SSimon Glass        return PRIORITY_CALC + prio
129fc3fe1c2SSimon Glass
130bb1501f2SSimon Glass    def MakeEnvironment(self, full_path):
131fc3fe1c2SSimon Glass        """Returns an environment for using the toolchain.
132fc3fe1c2SSimon Glass
133bb1501f2SSimon Glass        Thie takes the current environment and adds CROSS_COMPILE so that
134bb1501f2SSimon Glass        the tool chain will operate correctly.
135bb1501f2SSimon Glass
136bb1501f2SSimon Glass        Args:
137bb1501f2SSimon Glass            full_path: Return the full path in CROSS_COMPILE and don't set
138bb1501f2SSimon Glass                PATH
139fc3fe1c2SSimon Glass        """
140fc3fe1c2SSimon Glass        env = dict(os.environ)
141bb1501f2SSimon Glass        if full_path:
142bb1501f2SSimon Glass            env['CROSS_COMPILE'] = os.path.join(self.path, self.cross)
143bb1501f2SSimon Glass        else:
144fc3fe1c2SSimon Glass            env['CROSS_COMPILE'] = self.cross
145f210b587SSimon Glass            env['PATH'] = self.path + ':' + env['PATH']
146bb1501f2SSimon Glass
147fc3fe1c2SSimon Glass        return env
148fc3fe1c2SSimon Glass
149fc3fe1c2SSimon Glass
150fc3fe1c2SSimon Glassclass Toolchains:
151fc3fe1c2SSimon Glass    """Manage a list of toolchains for building U-Boot
152fc3fe1c2SSimon Glass
153fc3fe1c2SSimon Glass    We select one toolchain for each architecture type
154fc3fe1c2SSimon Glass
155fc3fe1c2SSimon Glass    Public members:
156fc3fe1c2SSimon Glass        toolchains: Dict of Toolchain objects, keyed by architecture name
15717bce66cSSimon Glass        prefixes: Dict of prefixes to check, keyed by architecture. This can
15817bce66cSSimon Glass            be a full path and toolchain prefix, for example
15917bce66cSSimon Glass            {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of
16017bce66cSSimon Glass            something on the search path, for example
16117bce66cSSimon Glass            {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported.
162fc3fe1c2SSimon Glass        paths: List of paths to check for toolchains (may contain wildcards)
163fc3fe1c2SSimon Glass    """
164fc3fe1c2SSimon Glass
165fc3fe1c2SSimon Glass    def __init__(self):
166fc3fe1c2SSimon Glass        self.toolchains = {}
16717bce66cSSimon Glass        self.prefixes = {}
168fc3fe1c2SSimon Glass        self.paths = []
169d4144e45SSimon Glass        self._make_flags = dict(bsettings.GetItems('make-flags'))
170d4144e45SSimon Glass
17180e6a487SSimon Glass    def GetPathList(self, show_warning=True):
172827e37b5SSimon Glass        """Get a list of available toolchain paths
173827e37b5SSimon Glass
17480e6a487SSimon Glass        Args:
17580e6a487SSimon Glass            show_warning: True to show a warning if there are no tool chains.
17680e6a487SSimon Glass
177827e37b5SSimon Glass        Returns:
178827e37b5SSimon Glass            List of strings, each a path to a toolchain mentioned in the
179827e37b5SSimon Glass            [toolchain] section of the settings file.
180827e37b5SSimon Glass        """
1814281ad8eSSimon Glass        toolchains = bsettings.GetItems('toolchain')
18280e6a487SSimon Glass        if show_warning and not toolchains:
183*713bea38SSimon Glass            print ("Warning: No tool chains. Please run 'buildman "
184*713bea38SSimon Glass                   "--fetch-arch all' to download all available toolchains, or "
185*713bea38SSimon Glass                   "add a [toolchain] section to your buildman config file "
186*713bea38SSimon Glass                   "%s. See README for details" %
1871826a18dSMasahiro Yamada                   bsettings.config_fname)
1884281ad8eSSimon Glass
189827e37b5SSimon Glass        paths = []
1904281ad8eSSimon Glass        for name, value in toolchains:
191fc3fe1c2SSimon Glass            if '*' in value:
192827e37b5SSimon Glass                paths += glob.glob(value)
193fc3fe1c2SSimon Glass            else:
194827e37b5SSimon Glass                paths.append(value)
195827e37b5SSimon Glass        return paths
196827e37b5SSimon Glass
19780e6a487SSimon Glass    def GetSettings(self, show_warning=True):
19880e6a487SSimon Glass        """Get toolchain settings from the settings file.
19980e6a487SSimon Glass
20080e6a487SSimon Glass        Args:
20180e6a487SSimon Glass            show_warning: True to show a warning if there are no tool chains.
20280e6a487SSimon Glass        """
20317bce66cSSimon Glass        self.prefixes = bsettings.GetItems('toolchain-prefix')
20480e6a487SSimon Glass        self.paths += self.GetPathList(show_warning)
205fc3fe1c2SSimon Glass
206608e399fSSimon Glass    def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC,
207608e399fSSimon Glass            arch=None):
208fc3fe1c2SSimon Glass        """Add a toolchain to our list
209fc3fe1c2SSimon Glass
210fc3fe1c2SSimon Glass        We select the given toolchain as our preferred one for its
211fc3fe1c2SSimon Glass        architecture if it is a higher priority than the others.
212fc3fe1c2SSimon Glass
213fc3fe1c2SSimon Glass        Args:
214fc3fe1c2SSimon Glass            fname: Filename of toolchain's gcc driver
215fc3fe1c2SSimon Glass            test: True to run the toolchain to test it
216ff690df9SSimon Glass            priority: Priority to use for this toolchain
217608e399fSSimon Glass            arch: Toolchain architecture, or None if not known
218fc3fe1c2SSimon Glass        """
219608e399fSSimon Glass        toolchain = Toolchain(fname, test, verbose, priority, arch)
220fc3fe1c2SSimon Glass        add_it = toolchain.ok
221fc3fe1c2SSimon Glass        if toolchain.arch in self.toolchains:
222fc3fe1c2SSimon Glass            add_it = (toolchain.priority <
223fc3fe1c2SSimon Glass                        self.toolchains[toolchain.arch].priority)
224fc3fe1c2SSimon Glass        if add_it:
225fc3fe1c2SSimon Glass            self.toolchains[toolchain.arch] = toolchain
226ff690df9SSimon Glass        elif verbose:
227ff690df9SSimon Glass            print ("Toolchain '%s' at priority %d will be ignored because "
228ff690df9SSimon Glass                   "another toolchain for arch '%s' has priority %d" %
229ff690df9SSimon Glass                   (toolchain.gcc, toolchain.priority, toolchain.arch,
230ff690df9SSimon Glass                    self.toolchains[toolchain.arch].priority))
231fc3fe1c2SSimon Glass
232827e37b5SSimon Glass    def ScanPath(self, path, verbose):
233827e37b5SSimon Glass        """Scan a path for a valid toolchain
234827e37b5SSimon Glass
235827e37b5SSimon Glass        Args:
236827e37b5SSimon Glass            path: Path to scan
237827e37b5SSimon Glass            verbose: True to print out progress information
238827e37b5SSimon Glass        Returns:
239827e37b5SSimon Glass            Filename of C compiler if found, else None
240827e37b5SSimon Glass        """
241d9088983SAlbert ARIBAUD        fnames = []
242827e37b5SSimon Glass        for subdir in ['.', 'bin', 'usr/bin']:
243827e37b5SSimon Glass            dirname = os.path.join(path, subdir)
244827e37b5SSimon Glass            if verbose: print "      - looking in '%s'" % dirname
245827e37b5SSimon Glass            for fname in glob.glob(dirname + '/*gcc'):
246827e37b5SSimon Glass                if verbose: print "         - found '%s'" % fname
247d9088983SAlbert ARIBAUD                fnames.append(fname)
248d9088983SAlbert ARIBAUD        return fnames
249827e37b5SSimon Glass
25017bce66cSSimon Glass    def ScanPathEnv(self, fname):
25117bce66cSSimon Glass        """Scan the PATH environment variable for a given filename.
25217bce66cSSimon Glass
25317bce66cSSimon Glass        Args:
25417bce66cSSimon Glass            fname: Filename to scan for
25517bce66cSSimon Glass        Returns:
25617bce66cSSimon Glass            List of matching pathanames, or [] if none
25717bce66cSSimon Glass        """
25817bce66cSSimon Glass        pathname_list = []
25917bce66cSSimon Glass        for path in os.environ["PATH"].split(os.pathsep):
26017bce66cSSimon Glass            path = path.strip('"')
26117bce66cSSimon Glass            pathname = os.path.join(path, fname)
26217bce66cSSimon Glass            if os.path.exists(pathname):
26317bce66cSSimon Glass                pathname_list.append(pathname)
26417bce66cSSimon Glass        return pathname_list
265827e37b5SSimon Glass
266fc3fe1c2SSimon Glass    def Scan(self, verbose):
267fc3fe1c2SSimon Glass        """Scan for available toolchains and select the best for each arch.
268fc3fe1c2SSimon Glass
269fc3fe1c2SSimon Glass        We look for all the toolchains we can file, figure out the
270fc3fe1c2SSimon Glass        architecture for each, and whether it works. Then we select the
271fc3fe1c2SSimon Glass        highest priority toolchain for each arch.
272fc3fe1c2SSimon Glass
273fc3fe1c2SSimon Glass        Args:
274fc3fe1c2SSimon Glass            verbose: True to print out progress information
275fc3fe1c2SSimon Glass        """
276fc3fe1c2SSimon Glass        if verbose: print 'Scanning for tool chains'
27717bce66cSSimon Glass        for name, value in self.prefixes:
27817bce66cSSimon Glass            if verbose: print "   - scanning prefix '%s'" % value
27917bce66cSSimon Glass            if os.path.exists(value):
28017bce66cSSimon Glass                self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name)
28117bce66cSSimon Glass                continue
28217bce66cSSimon Glass            fname = value + 'gcc'
28317bce66cSSimon Glass            if os.path.exists(fname):
28417bce66cSSimon Glass                self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name)
28517bce66cSSimon Glass                continue
28617bce66cSSimon Glass            fname_list = self.ScanPathEnv(fname)
28717bce66cSSimon Glass            for f in fname_list:
28817bce66cSSimon Glass                self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name)
28917bce66cSSimon Glass            if not fname_list:
29017bce66cSSimon Glass                raise ValueError, ("No tool chain found for prefix '%s'" %
29117bce66cSSimon Glass                                   value)
292fc3fe1c2SSimon Glass        for path in self.paths:
293fc3fe1c2SSimon Glass            if verbose: print "   - scanning path '%s'" % path
294d9088983SAlbert ARIBAUD            fnames = self.ScanPath(path, verbose)
295d9088983SAlbert ARIBAUD            for fname in fnames:
296fc3fe1c2SSimon Glass                self.Add(fname, True, verbose)
297fc3fe1c2SSimon Glass
298fc3fe1c2SSimon Glass    def List(self):
299fc3fe1c2SSimon Glass        """List out the selected toolchains for each architecture"""
300*713bea38SSimon Glass        col = terminal.Color()
301*713bea38SSimon Glass        print col.Color(col.BLUE, 'List of available toolchains (%d):' %
302*713bea38SSimon Glass                        len(self.toolchains))
303fc3fe1c2SSimon Glass        if len(self.toolchains):
304fc3fe1c2SSimon Glass            for key, value in sorted(self.toolchains.iteritems()):
305fc3fe1c2SSimon Glass                print '%-10s: %s' % (key, value.gcc)
306fc3fe1c2SSimon Glass        else:
307fc3fe1c2SSimon Glass            print 'None'
308fc3fe1c2SSimon Glass
309fc3fe1c2SSimon Glass    def Select(self, arch):
310fc3fe1c2SSimon Glass        """Returns the toolchain for a given architecture
311fc3fe1c2SSimon Glass
312fc3fe1c2SSimon Glass        Args:
313fc3fe1c2SSimon Glass            args: Name of architecture (e.g. 'arm', 'ppc_8xx')
314fc3fe1c2SSimon Glass
315fc3fe1c2SSimon Glass        returns:
316fc3fe1c2SSimon Glass            toolchain object, or None if none found
317fc3fe1c2SSimon Glass        """
3189b83bfdcSSimon Glass        for tag, value in bsettings.GetItems('toolchain-alias'):
3199b83bfdcSSimon Glass            if arch == tag:
3209b83bfdcSSimon Glass                for alias in value.split():
3219b83bfdcSSimon Glass                    if alias in self.toolchains:
3229b83bfdcSSimon Glass                        return self.toolchains[alias]
323fc3fe1c2SSimon Glass
324fc3fe1c2SSimon Glass        if not arch in self.toolchains:
325fc3fe1c2SSimon Glass            raise ValueError, ("No tool chain found for arch '%s'" % arch)
326fc3fe1c2SSimon Glass        return self.toolchains[arch]
3274281ad8eSSimon Glass
3284281ad8eSSimon Glass    def ResolveReferences(self, var_dict, args):
3294281ad8eSSimon Glass        """Resolve variable references in a string
3304281ad8eSSimon Glass
3314281ad8eSSimon Glass        This converts ${blah} within the string to the value of blah.
3324281ad8eSSimon Glass        This function works recursively.
3334281ad8eSSimon Glass
3344281ad8eSSimon Glass        Args:
3354281ad8eSSimon Glass            var_dict: Dictionary containing variables and their values
3364281ad8eSSimon Glass            args: String containing make arguments
3374281ad8eSSimon Glass        Returns:
3384281ad8eSSimon Glass            Resolved string
3394281ad8eSSimon Glass
3404281ad8eSSimon Glass        >>> bsettings.Setup()
3414281ad8eSSimon Glass        >>> tcs = Toolchains()
3424281ad8eSSimon Glass        >>> tcs.Add('fred', False)
3434281ad8eSSimon Glass        >>> var_dict = {'oblique' : 'OBLIQUE', 'first' : 'fi${second}rst', \
3444281ad8eSSimon Glass                        'second' : '2nd'}
3454281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set')
3464281ad8eSSimon Glass        'this=OBLIQUE_set'
3474281ad8eSSimon Glass        >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
3484281ad8eSSimon Glass        'this=OBLIQUE_setfi2ndrstnd'
3494281ad8eSSimon Glass        """
350f60c9d4fSSimon Glass        re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})')
3514281ad8eSSimon Glass
3524281ad8eSSimon Glass        while True:
3534281ad8eSSimon Glass            m = re_var.search(args)
3544281ad8eSSimon Glass            if not m:
3554281ad8eSSimon Glass                break
3564281ad8eSSimon Glass            lookup = m.group(0)[2:-1]
3574281ad8eSSimon Glass            value = var_dict.get(lookup, '')
3584281ad8eSSimon Glass            args = args[:m.start(0)] + value + args[m.end(0):]
3594281ad8eSSimon Glass        return args
3604281ad8eSSimon Glass
3614281ad8eSSimon Glass    def GetMakeArguments(self, board):
3624281ad8eSSimon Glass        """Returns 'make' arguments for a given board
3634281ad8eSSimon Glass
3644281ad8eSSimon Glass        The flags are in a section called 'make-flags'. Flags are named
3654281ad8eSSimon Glass        after the target they represent, for example snapper9260=TESTING=1
3664281ad8eSSimon Glass        will pass TESTING=1 to make when building the snapper9260 board.
3674281ad8eSSimon Glass
3684281ad8eSSimon Glass        References to other boards can be added in the string also. For
3694281ad8eSSimon Glass        example:
3704281ad8eSSimon Glass
3714281ad8eSSimon Glass        [make-flags]
3724281ad8eSSimon Glass        at91-boards=ENABLE_AT91_TEST=1
3734281ad8eSSimon Glass        snapper9260=${at91-boards} BUILD_TAG=442
3744281ad8eSSimon Glass        snapper9g45=${at91-boards} BUILD_TAG=443
3754281ad8eSSimon Glass
3764281ad8eSSimon Glass        This will return 'ENABLE_AT91_TEST=1 BUILD_TAG=442' for snapper9260
3774281ad8eSSimon Glass        and 'ENABLE_AT91_TEST=1 BUILD_TAG=443' for snapper9g45.
3784281ad8eSSimon Glass
3794281ad8eSSimon Glass        A special 'target' variable is set to the board target.
3804281ad8eSSimon Glass
3814281ad8eSSimon Glass        Args:
3824281ad8eSSimon Glass            board: Board object for the board to check.
3834281ad8eSSimon Glass        Returns:
3844281ad8eSSimon Glass            'make' flags for that board, or '' if none
3854281ad8eSSimon Glass        """
3864281ad8eSSimon Glass        self._make_flags['target'] = board.target
3874281ad8eSSimon Glass        arg_str = self.ResolveReferences(self._make_flags,
3884281ad8eSSimon Glass                           self._make_flags.get(board.target, ''))
3894281ad8eSSimon Glass        args = arg_str.split(' ')
3904281ad8eSSimon Glass        i = 0
3914281ad8eSSimon Glass        while i < len(args):
3924281ad8eSSimon Glass            if not args[i]:
3934281ad8eSSimon Glass                del args[i]
3944281ad8eSSimon Glass            else:
3954281ad8eSSimon Glass                i += 1
3964281ad8eSSimon Glass        return args
397827e37b5SSimon Glass
398827e37b5SSimon Glass    def LocateArchUrl(self, fetch_arch):
399827e37b5SSimon Glass        """Find a toolchain available online
400827e37b5SSimon Glass
401827e37b5SSimon Glass        Look in standard places for available toolchains. At present the
402827e37b5SSimon Glass        only standard place is at kernel.org.
403827e37b5SSimon Glass
404827e37b5SSimon Glass        Args:
405827e37b5SSimon Glass            arch: Architecture to look for, or 'list' for all
406827e37b5SSimon Glass        Returns:
407827e37b5SSimon Glass            If fetch_arch is 'list', a tuple:
408827e37b5SSimon Glass                Machine architecture (e.g. x86_64)
409827e37b5SSimon Glass                List of toolchains
410827e37b5SSimon Glass            else
411827e37b5SSimon Glass                URL containing this toolchain, if avaialble, else None
412827e37b5SSimon Glass        """
413827e37b5SSimon Glass        arch = command.OutputOneLine('uname', '-m')
414827e37b5SSimon Glass        base = 'https://www.kernel.org/pub/tools/crosstool/files/bin'
4151246231cSMichal Simek        versions = ['4.9.0', '4.6.3', '4.6.2', '4.5.1', '4.2.4']
416827e37b5SSimon Glass        links = []
417827e37b5SSimon Glass        for version in versions:
418827e37b5SSimon Glass            url = '%s/%s/%s/' % (base, arch, version)
419827e37b5SSimon Glass            print 'Checking: %s' % url
420827e37b5SSimon Glass            response = urllib2.urlopen(url)
421827e37b5SSimon Glass            html = response.read()
422827e37b5SSimon Glass            parser = MyHTMLParser(fetch_arch)
423827e37b5SSimon Glass            parser.feed(html)
424827e37b5SSimon Glass            if fetch_arch == 'list':
425827e37b5SSimon Glass                links += parser.links
426827e37b5SSimon Glass            elif parser.arch_link:
427827e37b5SSimon Glass                return url + parser.arch_link
428827e37b5SSimon Glass        if fetch_arch == 'list':
429827e37b5SSimon Glass            return arch, links
430827e37b5SSimon Glass        return None
431827e37b5SSimon Glass
432827e37b5SSimon Glass    def Download(self, url):
433827e37b5SSimon Glass        """Download a file to a temporary directory
434827e37b5SSimon Glass
435827e37b5SSimon Glass        Args:
436827e37b5SSimon Glass            url: URL to download
437827e37b5SSimon Glass        Returns:
438827e37b5SSimon Glass            Tuple:
439827e37b5SSimon Glass                Temporary directory name
440827e37b5SSimon Glass                Full path to the downloaded archive file in that directory,
441827e37b5SSimon Glass                    or None if there was an error while downloading
442827e37b5SSimon Glass        """
443ad24ebacSSimon Glass        print 'Downloading: %s' % url
444827e37b5SSimon Glass        leaf = url.split('/')[-1]
445827e37b5SSimon Glass        tmpdir = tempfile.mkdtemp('.buildman')
446827e37b5SSimon Glass        response = urllib2.urlopen(url)
447827e37b5SSimon Glass        fname = os.path.join(tmpdir, leaf)
448827e37b5SSimon Glass        fd = open(fname, 'wb')
449827e37b5SSimon Glass        meta = response.info()
450ad24ebacSSimon Glass        size = int(meta.getheaders('Content-Length')[0])
451827e37b5SSimon Glass        done = 0
452827e37b5SSimon Glass        block_size = 1 << 16
453827e37b5SSimon Glass        status = ''
454827e37b5SSimon Glass
455827e37b5SSimon Glass        # Read the file in chunks and show progress as we go
456827e37b5SSimon Glass        while True:
457827e37b5SSimon Glass            buffer = response.read(block_size)
458827e37b5SSimon Glass            if not buffer:
459827e37b5SSimon Glass                print chr(8) * (len(status) + 1), '\r',
460827e37b5SSimon Glass                break
461827e37b5SSimon Glass
462827e37b5SSimon Glass            done += len(buffer)
463827e37b5SSimon Glass            fd.write(buffer)
464ad24ebacSSimon Glass            status = r'%10d MiB  [%3d%%]' % (done / 1024 / 1024,
465827e37b5SSimon Glass                                             done * 100 / size)
466827e37b5SSimon Glass            status = status + chr(8) * (len(status) + 1)
467827e37b5SSimon Glass            print status,
468827e37b5SSimon Glass            sys.stdout.flush()
469827e37b5SSimon Glass        fd.close()
470827e37b5SSimon Glass        if done != size:
471827e37b5SSimon Glass            print 'Error, failed to download'
472827e37b5SSimon Glass            os.remove(fname)
473827e37b5SSimon Glass            fname = None
474827e37b5SSimon Glass        return tmpdir, fname
475827e37b5SSimon Glass
476827e37b5SSimon Glass    def Unpack(self, fname, dest):
477827e37b5SSimon Glass        """Unpack a tar file
478827e37b5SSimon Glass
479827e37b5SSimon Glass        Args:
480827e37b5SSimon Glass            fname: Filename to unpack
481827e37b5SSimon Glass            dest: Destination directory
482827e37b5SSimon Glass        Returns:
483827e37b5SSimon Glass            Directory name of the first entry in the archive, without the
484827e37b5SSimon Glass            trailing /
485827e37b5SSimon Glass        """
486827e37b5SSimon Glass        stdout = command.Output('tar', 'xvfJ', fname, '-C', dest)
487827e37b5SSimon Glass        return stdout.splitlines()[0][:-1]
488827e37b5SSimon Glass
489827e37b5SSimon Glass    def TestSettingsHasPath(self, path):
490827e37b5SSimon Glass        """Check if builmand will find this toolchain
491827e37b5SSimon Glass
492827e37b5SSimon Glass        Returns:
493827e37b5SSimon Glass            True if the path is in settings, False if not
494827e37b5SSimon Glass        """
49580e6a487SSimon Glass        paths = self.GetPathList(False)
496827e37b5SSimon Glass        return path in paths
497827e37b5SSimon Glass
498827e37b5SSimon Glass    def ListArchs(self):
499827e37b5SSimon Glass        """List architectures with available toolchains to download"""
500827e37b5SSimon Glass        host_arch, archives = self.LocateArchUrl('list')
501827e37b5SSimon Glass        re_arch = re.compile('[-a-z0-9.]*_([^-]*)-.*')
502827e37b5SSimon Glass        arch_set = set()
503827e37b5SSimon Glass        for archive in archives:
504827e37b5SSimon Glass            # Remove the host architecture from the start
505827e37b5SSimon Glass            arch = re_arch.match(archive[len(host_arch):])
506827e37b5SSimon Glass            if arch:
507827e37b5SSimon Glass                arch_set.add(arch.group(1))
508827e37b5SSimon Glass        return sorted(arch_set)
509827e37b5SSimon Glass
510827e37b5SSimon Glass    def FetchAndInstall(self, arch):
511827e37b5SSimon Glass        """Fetch and install a new toolchain
512827e37b5SSimon Glass
513827e37b5SSimon Glass        arch:
514827e37b5SSimon Glass            Architecture to fetch, or 'list' to list
515827e37b5SSimon Glass        """
516827e37b5SSimon Glass        # Fist get the URL for this architecture
517*713bea38SSimon Glass        col = terminal.Color()
518*713bea38SSimon Glass        print col.Color(col.BLUE, "Downloading toolchain for arch '%s'" % arch)
519827e37b5SSimon Glass        url = self.LocateArchUrl(arch)
520827e37b5SSimon Glass        if not url:
521827e37b5SSimon Glass            print ("Cannot find toolchain for arch '%s' - use 'list' to list" %
522827e37b5SSimon Glass                   arch)
523827e37b5SSimon Glass            return 2
524827e37b5SSimon Glass        home = os.environ['HOME']
525827e37b5SSimon Glass        dest = os.path.join(home, '.buildman-toolchains')
526827e37b5SSimon Glass        if not os.path.exists(dest):
527827e37b5SSimon Glass            os.mkdir(dest)
528827e37b5SSimon Glass
529827e37b5SSimon Glass        # Download the tar file for this toolchain and unpack it
530827e37b5SSimon Glass        tmpdir, tarfile = self.Download(url)
531827e37b5SSimon Glass        if not tarfile:
532827e37b5SSimon Glass            return 1
533*713bea38SSimon Glass        print col.Color(col.GREEN, 'Unpacking to: %s' % dest),
534827e37b5SSimon Glass        sys.stdout.flush()
535827e37b5SSimon Glass        path = self.Unpack(tarfile, dest)
536827e37b5SSimon Glass        os.remove(tarfile)
537827e37b5SSimon Glass        os.rmdir(tmpdir)
538827e37b5SSimon Glass        print
539827e37b5SSimon Glass
540827e37b5SSimon Glass        # Check that the toolchain works
541*713bea38SSimon Glass        print col.Color(col.GREEN, 'Testing')
542827e37b5SSimon Glass        dirpath = os.path.join(dest, path)
5432a76a649SSimon Glass        compiler_fname_list = self.ScanPath(dirpath, True)
5442a76a649SSimon Glass        if not compiler_fname_list:
545827e37b5SSimon Glass            print 'Could not locate C compiler - fetch failed.'
546827e37b5SSimon Glass            return 1
5472a76a649SSimon Glass        if len(compiler_fname_list) != 1:
548*713bea38SSimon Glass            print col.Color(col.RED, 'Warning, ambiguous toolchains: %s' %
549*713bea38SSimon Glass                            ', '.join(compiler_fname_list))
5502a76a649SSimon Glass        toolchain = Toolchain(compiler_fname_list[0], True, True)
551827e37b5SSimon Glass
552827e37b5SSimon Glass        # Make sure that it will be found by buildman
553827e37b5SSimon Glass        if not self.TestSettingsHasPath(dirpath):
554827e37b5SSimon Glass            print ("Adding 'download' to config file '%s'" %
555827e37b5SSimon Glass                   bsettings.config_fname)
556827e37b5SSimon Glass            tools_dir = os.path.dirname(dirpath)
557827e37b5SSimon Glass            bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir)
558827e37b5SSimon Glass        return 0
559