diff options
Diffstat (limited to 'utils/google-benchmark/mingw.py')
| -rw-r--r-- | utils/google-benchmark/mingw.py | 320 | 
1 files changed, 320 insertions, 0 deletions
| diff --git a/utils/google-benchmark/mingw.py b/utils/google-benchmark/mingw.py new file mode 100644 index 000000000000..706ad559db9c --- /dev/null +++ b/utils/google-benchmark/mingw.py @@ -0,0 +1,320 @@ +#! /usr/bin/env python +# encoding: utf-8 + +import argparse +import errno +import logging +import os +import platform +import re +import sys +import subprocess +import tempfile + +try: +    import winreg +except ImportError: +    import _winreg as winreg +try: +    import urllib.request as request +except ImportError: +    import urllib as request +try: +    import urllib.parse as parse +except ImportError: +    import urlparse as parse + +class EmptyLogger(object): +    ''' +    Provides an implementation that performs no logging +    ''' +    def debug(self, *k, **kw): +        pass +    def info(self, *k, **kw): +        pass +    def warn(self, *k, **kw): +        pass +    def error(self, *k, **kw): +        pass +    def critical(self, *k, **kw): +        pass +    def setLevel(self, *k, **kw): +        pass + +urls = ( +    'http://downloads.sourceforge.net/project/mingw-w64/Toolchains%20' +        'targetting%20Win32/Personal%20Builds/mingw-builds/installer/' +        'repository.txt', +    'http://downloads.sourceforge.net/project/mingwbuilds/host-windows/' +        'repository.txt' +) +''' +A list of mingw-build repositories +''' + +def repository(urls = urls, log = EmptyLogger()): +    ''' +    Downloads and parse mingw-build repository files and parses them +    ''' +    log.info('getting mingw-builds repository') +    versions = {} +    re_sourceforge = re.compile(r'http://sourceforge.net/projects/([^/]+)/files') +    re_sub = r'http://downloads.sourceforge.net/project/\1' +    for url in urls: +        log.debug(' - requesting: %s', url) +        socket = request.urlopen(url) +        repo = socket.read() +        if not isinstance(repo, str): +            repo = repo.decode(); +        socket.close() +        for entry in repo.split('\n')[:-1]: +            value = entry.split('|') +            version = tuple([int(n) for n in value[0].strip().split('.')]) +            version = versions.setdefault(version, {}) +            arch = value[1].strip() +            if arch == 'x32': +                arch = 'i686' +            elif arch == 'x64': +                arch = 'x86_64' +            arch = version.setdefault(arch, {}) +            threading = arch.setdefault(value[2].strip(), {}) +            exceptions = threading.setdefault(value[3].strip(), {}) +            revision = exceptions.setdefault(int(value[4].strip()[3:]), +                re_sourceforge.sub(re_sub, value[5].strip())) +    return versions + +def find_in_path(file, path=None): +    ''' +    Attempts to find an executable in the path +    ''' +    if platform.system() == 'Windows': +        file += '.exe' +    if path is None: +        path = os.environ.get('PATH', '') +    if type(path) is type(''): +        path = path.split(os.pathsep) +    return list(filter(os.path.exists, +        map(lambda dir, file=file: os.path.join(dir, file), path))) + +def find_7zip(log = EmptyLogger()): +    ''' +    Attempts to find 7zip for unpacking the mingw-build archives +    ''' +    log.info('finding 7zip') +    path = find_in_path('7z') +    if not path: +        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\7-Zip') +        path, _ = winreg.QueryValueEx(key, 'Path') +        path = [os.path.join(path, '7z.exe')] +    log.debug('found \'%s\'', path[0]) +    return path[0] + +find_7zip() + +def unpack(archive, location, log = EmptyLogger()): +    ''' +    Unpacks a mingw-builds archive +    ''' +    sevenzip = find_7zip(log) +    log.info('unpacking %s', os.path.basename(archive)) +    cmd = [sevenzip, 'x', archive, '-o' + location, '-y'] +    log.debug(' - %r', cmd) +    with open(os.devnull, 'w') as devnull: +        subprocess.check_call(cmd, stdout = devnull) + +def download(url, location, log = EmptyLogger()): +    ''' +    Downloads and unpacks a mingw-builds archive +    ''' +    log.info('downloading MinGW') +    log.debug(' - url: %s', url) +    log.debug(' - location: %s', location) + +    re_content = re.compile(r'attachment;[ \t]*filename=(")?([^"]*)(")?[\r\n]*') + +    stream = request.urlopen(url) +    try: +        content = stream.getheader('Content-Disposition') or '' +    except AttributeError: +        content = stream.headers.getheader('Content-Disposition') or '' +    matches = re_content.match(content) +    if matches: +        filename = matches.group(2) +    else: +        parsed = parse.urlparse(stream.geturl()) +        filename = os.path.basename(parsed.path) + +    try: +        os.makedirs(location) +    except OSError as e: +        if e.errno == errno.EEXIST and os.path.isdir(location): +            pass +        else: +            raise + +    archive = os.path.join(location, filename) +    with open(archive, 'wb') as out: +        while True: +            buf = stream.read(1024) +            if not buf: +                break +            out.write(buf) +    unpack(archive, location, log = log) +    os.remove(archive) + +    possible = os.path.join(location, 'mingw64') +    if not os.path.exists(possible): +        possible = os.path.join(location, 'mingw32') +        if not os.path.exists(possible): +            raise ValueError('Failed to find unpacked MinGW: ' + possible) +    return possible + +def root(location = None, arch = None, version = None, threading = None, +        exceptions = None, revision = None, log = EmptyLogger()): +    ''' +    Returns the root folder of a specific version of the mingw-builds variant +    of gcc. Will download the compiler if needed +    ''' + +    # Get the repository if we don't have all the information +    if not (arch and version and threading and exceptions and revision): +        versions = repository(log = log) + +    # Determine some defaults +    version = version or max(versions.keys()) +    if not arch: +        arch = platform.machine().lower() +        if arch == 'x86': +            arch = 'i686' +        elif arch == 'amd64': +            arch = 'x86_64' +    if not threading: +        keys = versions[version][arch].keys() +        if 'posix' in keys: +            threading = 'posix' +        elif 'win32' in keys: +            threading = 'win32' +        else: +            threading = keys[0] +    if not exceptions: +        keys = versions[version][arch][threading].keys() +        if 'seh' in keys: +            exceptions = 'seh' +        elif 'sjlj' in keys: +            exceptions = 'sjlj' +        else: +            exceptions = keys[0] +    if revision == None: +        revision = max(versions[version][arch][threading][exceptions].keys()) +    if not location: +        location = os.path.join(tempfile.gettempdir(), 'mingw-builds') + +    # Get the download url +    url = versions[version][arch][threading][exceptions][revision] + +    # Tell the user whatzzup +    log.info('finding MinGW %s', '.'.join(str(v) for v in version)) +    log.debug(' - arch: %s', arch) +    log.debug(' - threading: %s', threading) +    log.debug(' - exceptions: %s', exceptions) +    log.debug(' - revision: %s', revision) +    log.debug(' - url: %s', url) + +    # Store each specific revision differently +    slug = '{version}-{arch}-{threading}-{exceptions}-rev{revision}' +    slug = slug.format( +        version = '.'.join(str(v) for v in version), +        arch = arch, +        threading = threading, +        exceptions = exceptions, +        revision = revision +    ) +    if arch == 'x86_64': +        root_dir = os.path.join(location, slug, 'mingw64') +    elif arch == 'i686': +        root_dir = os.path.join(location, slug, 'mingw32') +    else: +        raise ValueError('Unknown MinGW arch: ' + arch) + +    # Download if needed +    if not os.path.exists(root_dir): +        downloaded = download(url, os.path.join(location, slug), log = log) +        if downloaded != root_dir: +            raise ValueError('The location of mingw did not match\n%s\n%s' +                % (downloaded, root_dir)) + +    return root_dir + +def str2ver(string): +    ''' +    Converts a version string into a tuple +    ''' +    try: +        version = tuple(int(v) for v in string.split('.')) +        if len(version) is not 3: +            raise ValueError() +    except ValueError: +        raise argparse.ArgumentTypeError( +            'please provide a three digit version string') +    return version + +def main(): +    ''' +    Invoked when the script is run directly by the python interpreter +    ''' +    parser = argparse.ArgumentParser( +        description = 'Downloads a specific version of MinGW', +        formatter_class = argparse.ArgumentDefaultsHelpFormatter +    ) +    parser.add_argument('--location', +        help = 'the location to download the compiler to', +        default = os.path.join(tempfile.gettempdir(), 'mingw-builds')) +    parser.add_argument('--arch', required = True, choices = ['i686', 'x86_64'], +        help = 'the target MinGW architecture string') +    parser.add_argument('--version', type = str2ver, +        help = 'the version of GCC to download') +    parser.add_argument('--threading', choices = ['posix', 'win32'], +        help = 'the threading type of the compiler') +    parser.add_argument('--exceptions', choices = ['sjlj', 'seh', 'dwarf'], +        help = 'the method to throw exceptions') +    parser.add_argument('--revision', type=int, +        help = 'the revision of the MinGW release') +    group = parser.add_mutually_exclusive_group() +    group.add_argument('-v', '--verbose', action='store_true', +        help='increase the script output verbosity') +    group.add_argument('-q', '--quiet', action='store_true', +        help='only print errors and warning') +    args = parser.parse_args() + +    # Create the logger +    logger = logging.getLogger('mingw') +    handler = logging.StreamHandler() +    formatter = logging.Formatter('%(message)s') +    handler.setFormatter(formatter) +    logger.addHandler(handler) +    logger.setLevel(logging.INFO) +    if args.quiet: +        logger.setLevel(logging.WARN) +    if args.verbose: +        logger.setLevel(logging.DEBUG) + +    # Get MinGW +    root_dir = root(location = args.location, arch = args.arch, +        version = args.version, threading = args.threading, +        exceptions = args.exceptions, revision = args.revision, +        log = logger) + +    sys.stdout.write('%s\n' % os.path.join(root_dir, 'bin')) + +if __name__ == '__main__': +    try: +        main() +    except IOError as e: +        sys.stderr.write('IO error: %s\n' % e) +        sys.exit(1) +    except OSError as e: +        sys.stderr.write('OS error: %s\n' % e) +        sys.exit(1) +    except KeyboardInterrupt as e: +        sys.stderr.write('Killed\n') +        sys.exit(1) | 
