X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=tools%2Fbuildman%2Ftoolchain.py;h=4b35f400e97d61cd9bc7d445b4c378e6e246d5d5;hb=97294a48c73ae16a03bce4b624334545e890e851;hp=051da11ef01de007afc59bd152cd70c24d8cbc3c;hpb=a538ae997ae4cb375ba5362b50ba3f5b35f9519b;p=oweals%2Fu-boot.git diff --git a/tools/buildman/toolchain.py b/tools/buildman/toolchain.py index 051da11ef0..4b35f400e9 100644 --- a/tools/buildman/toolchain.py +++ b/tools/buildman/toolchain.py @@ -1,7 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ # Copyright (c) 2012 The Chromium OS Authors. # -# SPDX-License-Identifier: GPL-2.0+ -# import re import glob @@ -13,6 +12,10 @@ import urllib2 import bsettings import command +import terminal + +(PRIORITY_FULL_PREFIX, PRIORITY_PREFIX_GCC, PRIORITY_PREFIX_GCC_PATH, + PRIORITY_CALC) = range(4) # Simple class to collect links from a page class MyHTMLParser(HTMLParser): @@ -29,7 +32,7 @@ class MyHTMLParser(HTMLParser): HTMLParser.__init__(self) self.arch_link = None self.links = [] - self._match = '_%s-' % arch + self.re_arch = re.compile('[-_]%s-' % arch) def handle_starttag(self, tag, attrs): if tag == 'a': @@ -37,7 +40,7 @@ class MyHTMLParser(HTMLParser): if tag == 'href': if value and value.endswith('.xz'): self.links.append(value) - if self._match in value: + if self.re_arch.search(value): self.arch_link = value @@ -50,13 +53,18 @@ class Toolchain: cross: Cross compile string, e.g. 'arm-linux-' arch: Architecture of toolchain as determined from the first component of the filename. E.g. arm-linux-gcc becomes arm + priority: Toolchain priority (0=highest, 20=lowest) """ - def __init__(self, fname, test, verbose=False): + def __init__(self, fname, test, verbose=False, priority=PRIORITY_CALC, + arch=None): """Create a new toolchain object. Args: fname: Filename of the gcc component test: True to run the toolchain to test it + verbose: True to print out the information + priority: Priority to use for this toolchain, or PRIORITY_CALC to + calculate it """ self.gcc = fname self.path = os.path.dirname(fname) @@ -69,12 +77,19 @@ class Toolchain: # The architecture is the first part of the name pos = self.cross.find('-') - self.arch = self.cross[:pos] if pos != -1 else 'sandbox' + if arch: + self.arch = arch + else: + self.arch = self.cross[:pos] if pos != -1 else 'sandbox' env = self.MakeEnvironment(False) # As a basic sanity check, run the C compiler with --version cmd = [fname, '--version'] + if priority == PRIORITY_CALC: + self.priority = self.GetPriority(fname) + else: + self.priority = priority if test: result = command.RunPipe([cmd], capture=True, env=env, raise_on_error=False) @@ -82,7 +97,8 @@ class Toolchain: if verbose: print 'Tool chain test: ', if self.ok: - print 'OK' + print "OK, arch='%s', priority %d" % (self.arch, + self.priority) else: print 'BAD' print 'Command: ', cmd @@ -90,7 +106,6 @@ class Toolchain: print result.stderr else: self.ok = True - self.priority = self.GetPriority(fname) def GetPriority(self, fname): """Return the priority of the toolchain. @@ -101,33 +116,52 @@ class Toolchain: Args: fname: Filename of toolchain Returns: - Priority of toolchain, 0=highest, 20=lowest. + Priority of toolchain, PRIORITY_CALC=highest, 20=lowest. """ priority_list = ['-elf', '-unknown-linux-gnu', '-linux', - '-none-linux-gnueabi', '-uclinux', '-none-eabi', - '-gentoo-linux-gnu', '-linux-gnueabi', '-le-linux', '-uclinux'] + '-none-linux-gnueabi', '-none-linux-gnueabihf', '-uclinux', + '-none-eabi', '-gentoo-linux-gnu', '-linux-gnueabi', + '-linux-gnueabihf', '-le-linux', '-uclinux'] for prio in range(len(priority_list)): if priority_list[prio] in fname: - return prio - return prio + return PRIORITY_CALC + prio + return PRIORITY_CALC + prio + + def GetWrapper(self, show_warning=True): + """Get toolchain wrapper from the setting file. + """ + value = '' + for name, value in bsettings.GetItems('toolchain-wrapper'): + if not value: + print "Warning: Wrapper not found" + if value: + value = value + ' ' + + return value def MakeEnvironment(self, full_path): """Returns an environment for using the toolchain. Thie takes the current environment and adds CROSS_COMPILE so that - the tool chain will operate correctly. + the tool chain will operate correctly. This also disables localized + output and possibly unicode encoded output of all build tools by + adding LC_ALL=C. Args: full_path: Return the full path in CROSS_COMPILE and don't set PATH """ env = dict(os.environ) + wrapper = self.GetWrapper() + if full_path: - env['CROSS_COMPILE'] = os.path.join(self.path, self.cross) + env['CROSS_COMPILE'] = wrapper + os.path.join(self.path, self.cross) else: - env['CROSS_COMPILE'] = self.cross + env['CROSS_COMPILE'] = wrapper + self.cross env['PATH'] = self.path + ':' + env['PATH'] + env['LC_ALL'] = 'C' + return env @@ -138,26 +172,37 @@ class Toolchains: Public members: toolchains: Dict of Toolchain objects, keyed by architecture name + prefixes: Dict of prefixes to check, keyed by architecture. This can + be a full path and toolchain prefix, for example + {'x86', 'opt/i386-linux/bin/i386-linux-'}, or the name of + something on the search path, for example + {'arm', 'arm-linux-gnueabihf-'}. Wildcards are not supported. paths: List of paths to check for toolchains (may contain wildcards) """ def __init__(self): self.toolchains = {} + self.prefixes = {} self.paths = [] self._make_flags = dict(bsettings.GetItems('make-flags')) - def GetPathList(self): + def GetPathList(self, show_warning=True): """Get a list of available toolchain paths + Args: + show_warning: True to show a warning if there are no tool chains. + Returns: List of strings, each a path to a toolchain mentioned in the [toolchain] section of the settings file. """ toolchains = bsettings.GetItems('toolchain') - if not toolchains: - print ("Warning: No tool chains - please add a [toolchain] section" - " to your buildman config file %s. See README for details" % - bsettings.config_fname) + if show_warning and not toolchains: + print ("Warning: No tool chains. Please run 'buildman " + "--fetch-arch all' to download all available toolchains, or " + "add a [toolchain] section to your buildman config file " + "%s. See README for details" % + bsettings.config_fname) paths = [] for name, value in toolchains: @@ -167,10 +212,17 @@ class Toolchains: paths.append(value) return paths - def GetSettings(self): - self.paths += self.GetPathList() + def GetSettings(self, show_warning=True): + """Get toolchain settings from the settings file. + + Args: + show_warning: True to show a warning if there are no tool chains. + """ + self.prefixes = bsettings.GetItems('toolchain-prefix') + self.paths += self.GetPathList(show_warning) - def Add(self, fname, test=True, verbose=False): + def Add(self, fname, test=True, verbose=False, priority=PRIORITY_CALC, + arch=None): """Add a toolchain to our list We select the given toolchain as our preferred one for its @@ -179,14 +231,21 @@ class Toolchains: Args: fname: Filename of toolchain's gcc driver test: True to run the toolchain to test it + priority: Priority to use for this toolchain + arch: Toolchain architecture, or None if not known """ - toolchain = Toolchain(fname, test, verbose) + toolchain = Toolchain(fname, test, verbose, priority, arch) add_it = toolchain.ok if toolchain.arch in self.toolchains: add_it = (toolchain.priority < self.toolchains[toolchain.arch].priority) if add_it: self.toolchains[toolchain.arch] = toolchain + elif verbose: + print ("Toolchain '%s' at priority %d will be ignored because " + "another toolchain for arch '%s' has priority %d" % + (toolchain.gcc, toolchain.priority, toolchain.arch, + self.toolchains[toolchain.arch].priority)) def ScanPath(self, path, verbose): """Scan a path for a valid toolchain @@ -206,6 +265,21 @@ class Toolchains: fnames.append(fname) return fnames + def ScanPathEnv(self, fname): + """Scan the PATH environment variable for a given filename. + + Args: + fname: Filename to scan for + Returns: + List of matching pathanames, or [] if none + """ + pathname_list = [] + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + pathname = os.path.join(path, fname) + if os.path.exists(pathname): + pathname_list.append(pathname) + return pathname_list def Scan(self, verbose): """Scan for available toolchains and select the best for each arch. @@ -218,6 +292,21 @@ class Toolchains: verbose: True to print out progress information """ if verbose: print 'Scanning for tool chains' + for name, value in self.prefixes: + if verbose: print " - scanning prefix '%s'" % value + if os.path.exists(value): + self.Add(value, True, verbose, PRIORITY_FULL_PREFIX, name) + continue + fname = value + 'gcc' + if os.path.exists(fname): + self.Add(fname, True, verbose, PRIORITY_PREFIX_GCC, name) + continue + fname_list = self.ScanPathEnv(fname) + for f in fname_list: + self.Add(f, True, verbose, PRIORITY_PREFIX_GCC_PATH, name) + if not fname_list: + raise ValueError, ("No tool chain found for prefix '%s'" % + value) for path in self.paths: if verbose: print " - scanning path '%s'" % path fnames = self.ScanPath(path, verbose) @@ -226,7 +315,9 @@ class Toolchains: def List(self): """List out the selected toolchains for each architecture""" - print 'List of available toolchains (%d):' % len(self.toolchains) + col = terminal.Color() + print col.Color(col.BLUE, 'List of available toolchains (%d):' % + len(self.toolchains)) if len(self.toolchains): for key, value in sorted(self.toolchains.iteritems()): print '%-10s: %s' % (key, value.gcc) @@ -339,7 +430,7 @@ class Toolchains: """ arch = command.OutputOneLine('uname', '-m') base = 'https://www.kernel.org/pub/tools/crosstool/files/bin' - versions = ['4.6.3', '4.6.2', '4.5.1', '4.2.4'] + versions = ['7.3.0', '6.4.0', '4.9.4'] links = [] for version in versions: url = '%s/%s/%s/' % (base, arch, version) @@ -367,14 +458,14 @@ class Toolchains: Full path to the downloaded archive file in that directory, or None if there was an error while downloading """ - print "Downloading: %s" % url + print 'Downloading: %s' % url leaf = url.split('/')[-1] tmpdir = tempfile.mkdtemp('.buildman') response = urllib2.urlopen(url) fname = os.path.join(tmpdir, leaf) fd = open(fname, 'wb') meta = response.info() - size = int(meta.getheaders("Content-Length")[0]) + size = int(meta.getheaders('Content-Length')[0]) done = 0 block_size = 1 << 16 status = '' @@ -388,7 +479,7 @@ class Toolchains: done += len(buffer) fd.write(buffer) - status = r"%10d MiB [%3d%%]" % (done / 1024 / 1024, + status = r'%10d MiB [%3d%%]' % (done / 1024 / 1024, done * 100 / size) status = status + chr(8) * (len(status) + 1) print status, @@ -414,12 +505,12 @@ class Toolchains: return stdout.splitlines()[0][:-1] def TestSettingsHasPath(self, path): - """Check if builmand will find this toolchain + """Check if buildman will find this toolchain Returns: True if the path is in settings, False if not """ - paths = self.GetPathList() + paths = self.GetPathList(False) return path in paths def ListArchs(self): @@ -441,6 +532,8 @@ class Toolchains: Architecture to fetch, or 'list' to list """ # Fist get the URL for this architecture + col = terminal.Color() + print col.Color(col.BLUE, "Downloading toolchain for arch '%s'" % arch) url = self.LocateArchUrl(arch) if not url: print ("Cannot find toolchain for arch '%s' - use 'list' to list" % @@ -455,7 +548,7 @@ class Toolchains: tmpdir, tarfile = self.Download(url) if not tarfile: return 1 - print 'Unpacking to: %s' % dest, + print col.Color(col.GREEN, 'Unpacking to: %s' % dest), sys.stdout.flush() path = self.Unpack(tarfile, dest) os.remove(tarfile) @@ -463,22 +556,20 @@ class Toolchains: print # Check that the toolchain works - print 'Testing' + print col.Color(col.GREEN, 'Testing') dirpath = os.path.join(dest, path) compiler_fname_list = self.ScanPath(dirpath, True) if not compiler_fname_list: print 'Could not locate C compiler - fetch failed.' return 1 if len(compiler_fname_list) != 1: - print ('Internal error, ambiguous toolchains: %s' % - (', '.join(compiler_fname))) - return 1 + print col.Color(col.RED, 'Warning, ambiguous toolchains: %s' % + ', '.join(compiler_fname_list)) toolchain = Toolchain(compiler_fname_list[0], True, True) # Make sure that it will be found by buildman if not self.TestSettingsHasPath(dirpath): print ("Adding 'download' to config file '%s'" % bsettings.config_fname) - tools_dir = os.path.dirname(dirpath) - bsettings.SetItem('toolchain', 'download', '%s/*' % tools_dir) + bsettings.SetItem('toolchain', 'download', '%s/*/*' % dest) return 0