160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson#! /usr/bin/env python
260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson# encoding: utf-8
360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport argparse
560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport errno
660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport logging
760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport os
860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport platform
960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport re
1060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport sys
1160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport subprocess
1260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonimport tempfile
1360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
1460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksontry:
1560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    import winreg
1660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonexcept ImportError:
1760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    import _winreg as winreg
1860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksontry:
1960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    import urllib.request as request
2060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonexcept ImportError:
2160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    import urllib as request
2260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksontry:
2360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    import urllib.parse as parse
2460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonexcept ImportError:
2560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    import urlparse as parse
2660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
2760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonclass EmptyLogger(object):
2860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
2960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Provides an implementation that performs no logging
3060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
3160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    def debug(self, *k, **kw):
3260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        pass
3360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    def info(self, *k, **kw):
3460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        pass
3560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    def warn(self, *k, **kw):
3660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        pass
3760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    def error(self, *k, **kw):
3860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        pass
3960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    def critical(self, *k, **kw):
4060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        pass
4160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    def setLevel(self, *k, **kw):
4260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        pass
4360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
4460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonurls = (
4560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    'http://downloads.sourceforge.net/project/mingw-w64/Toolchains%20'
4660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        'targetting%20Win32/Personal%20Builds/mingw-builds/installer/'
4760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        'repository.txt',
4860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    'http://downloads.sourceforge.net/project/mingwbuilds/host-windows/'
4960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        'repository.txt'
5060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson)
5160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson'''
5260910b3de7aa85ff28da14f34c7084711f99ac74Matt ClarksonA list of mingw-build repositories
5360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson'''
5460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
5560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksondef repository(urls = urls, log = EmptyLogger()):
5660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
5760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Downloads and parse mingw-build repository files and parses them
5860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
5960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.info('getting mingw-builds repository')
6060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    versions = {}
6160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    re_sourceforge = re.compile(r'http://sourceforge.net/projects/([^/]+)/files')
6260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    re_sub = r'http://downloads.sourceforge.net/project/\1'
6360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    for url in urls:
6460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        log.debug(' - requesting: %s', url)
6560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        socket = request.urlopen(url)
6660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        repo = socket.read()
6760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        if not isinstance(repo, str):
6860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            repo = repo.decode();
6960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        socket.close()
7060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        for entry in repo.split('\n')[:-1]:
7160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            value = entry.split('|')
7260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            version = tuple([int(n) for n in value[0].strip().split('.')])
7360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            version = versions.setdefault(version, {})
7460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            arch = value[1].strip()
7560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            if arch == 'x32':
7660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson                arch = 'i686'
7760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            elif arch == 'x64':
7860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson                arch = 'x86_64'
7960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            arch = version.setdefault(arch, {})
8060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            threading = arch.setdefault(value[2].strip(), {})
8160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            exceptions = threading.setdefault(value[3].strip(), {})
8260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            revision = exceptions.setdefault(int(value[4].strip()[3:]),
8360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson                re_sourceforge.sub(re_sub, value[5].strip()))
8460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    return versions
8560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
8660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksondef find_in_path(file, path=None):
8760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
8860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Attempts to find an executable in the path
8960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
9060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if platform.system() == 'Windows':
9160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        file += '.exe'
9260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if path is None:
9360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        path = os.environ.get('PATH', '')
9460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if type(path) is type(''):
9560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        path = path.split(os.pathsep)
9660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    return list(filter(os.path.exists,
9760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        map(lambda dir, file=file: os.path.join(dir, file), path)))
9860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
9960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksondef find_7zip(log = EmptyLogger()):
10060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
10160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Attempts to find 7zip for unpacking the mingw-build archives
10260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
10360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.info('finding 7zip')
10460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    path = find_in_path('7z')
10560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if not path:
10660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\7-Zip')
10760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        path, _ = winreg.QueryValueEx(key, 'Path')
10860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        path = [os.path.join(path, '7z.exe')]
10960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug('found \'%s\'', path[0])
11060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    return path[0]
11160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
11260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonfind_7zip()
11360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
11460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksondef unpack(archive, location, log = EmptyLogger()):
11560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
11660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Unpacks a mingw-builds archive
11760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
11860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    sevenzip = find_7zip(log)
11960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.info('unpacking %s', os.path.basename(archive))
12060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    cmd = [sevenzip, 'x', archive, '-o' + location, '-y']
12160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug(' - %r', cmd)
12260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    with open(os.devnull, 'w') as devnull:
12360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        subprocess.check_call(cmd, stdout = devnull)
12460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
12560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksondef download(url, location, log = EmptyLogger()):
12660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
12760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Downloads and unpacks a mingw-builds archive
12860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
12960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.info('downloading MinGW')
13060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug(' - url: %s', url)
13160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug(' - location: %s', location)
13260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
13360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    re_content = re.compile(r'attachment;[ \t]*filename=(")?([^"]*)(")?[\r\n]*')
13460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
13560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    stream = request.urlopen(url)
13660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    try:
13760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        content = stream.getheader('Content-Disposition') or ''
13860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    except AttributeError:
13960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        content = stream.headers.getheader('Content-Disposition') or ''
14060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    matches = re_content.match(content)
14160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if matches:
14260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        filename = matches.group(2)
14360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    else:
14460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        parsed = parse.urlparse(stream.geturl())
14560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        filename = os.path.basename(parsed.path)
14660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
14760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    try:
14860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        os.makedirs(location)
14960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    except OSError as e:
15060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        if e.errno == errno.EEXIST and os.path.isdir(location):
15160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            pass
15260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        else:
15360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            raise
15460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
15560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    archive = os.path.join(location, filename)
15660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    with open(archive, 'wb') as out:
15760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        while True:
15860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            buf = stream.read(1024)
15960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            if not buf:
16060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson                break
16160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            out.write(buf)
16260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    unpack(archive, location, log = log)
16360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    os.remove(archive)
16460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
16560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    possible = os.path.join(location, 'mingw64')
16660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if not os.path.exists(possible):
16760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        possible = os.path.join(location, 'mingw32')
16860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        if not os.path.exists(possible):
16960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            raise ValueError('Failed to find unpacked MinGW: ' + possible)
17060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    return possible
17160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
17260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksondef root(location = None, arch = None, version = None, threading = None,
17360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        exceptions = None, revision = None, log = EmptyLogger()):
17460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
17560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Returns the root folder of a specific version of the mingw-builds variant
17660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    of gcc. Will download the compiler if needed
17760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
17860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
17960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    # Get the repository if we don't have all the information
18060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if not (arch and version and threading and exceptions and revision):
18160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        versions = repository(log = log)
18260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
18360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    # Determine some defaults
18460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    version = version or max(versions.keys())
18560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if not arch:
18660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        arch = platform.machine().lower()
18760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        if arch == 'x86':
18860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            arch = 'i686'
18960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        elif arch == 'amd64':
19060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            arch = 'x86_64'
19160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if not threading:
19260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        keys = versions[version][arch].keys()
19360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        if 'posix' in keys:
19460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            threading = 'posix'
19560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        elif 'win32' in keys:
19660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            threading = 'win32'
19760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        else:
19860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            threading = keys[0]
19960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if not exceptions:
20060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        keys = versions[version][arch][threading].keys()
20160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        if 'seh' in keys:
20260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            exceptions = 'seh'
20360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        elif 'sjlj' in keys:
20460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            exceptions = 'sjlj'
20560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        else:
20660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            exceptions = keys[0]
20760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if revision == None:
20860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        revision = max(versions[version][arch][threading][exceptions].keys())
20960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if not location:
21060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        location = os.path.join(tempfile.gettempdir(), 'mingw-builds')
21160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
21260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    # Get the download url
21360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    url = versions[version][arch][threading][exceptions][revision]
21460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
21560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    # Tell the user whatzzup
21660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.info('finding MinGW %s', '.'.join(str(v) for v in version))
21760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug(' - arch: %s', arch)
21860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug(' - threading: %s', threading)
21960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug(' - exceptions: %s', exceptions)
22060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug(' - revision: %s', revision)
22160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    log.debug(' - url: %s', url)
22260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
22360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    # Store each specific revision differently
22460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    slug = '{version}-{arch}-{threading}-{exceptions}-rev{revision}'
22560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    slug = slug.format(
22660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        version = '.'.join(str(v) for v in version),
22760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        arch = arch,
22860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        threading = threading,
22960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        exceptions = exceptions,
23060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        revision = revision
23160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    )
23260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if arch == 'x86_64':
23360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        root_dir = os.path.join(location, slug, 'mingw64')
23460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    elif arch == 'i686':
23560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        root_dir = os.path.join(location, slug, 'mingw32')
23660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    else:
23760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        raise ValueError('Unknown MinGW arch: ' + arch)
23860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
23960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    # Download if needed
24060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if not os.path.exists(root_dir):
24160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        downloaded = download(url, os.path.join(location, slug), log = log)
24260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        if downloaded != root_dir:
24360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            raise ValueError('The location of mingw did not match\n%s\n%s'
24460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson                % (downloaded, root_dir))
24560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
24660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    return root_dir
24760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
24860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksondef str2ver(string):
24960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
25060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Converts a version string into a tuple
25160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
25260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    try:
25360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        version = tuple(int(v) for v in string.split('.'))
25460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        if len(version) is not 3:
25560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            raise ValueError()
25660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    except ValueError:
25760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        raise argparse.ArgumentTypeError(
25860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson            'please provide a three digit version string')
25960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    return version
26060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
26160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksondef main():
26260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
26360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    Invoked when the script is run directly by the python interpreter
26460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    '''
26560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    parser = argparse.ArgumentParser(
26660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        description = 'Downloads a specific version of MinGW',
26760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        formatter_class = argparse.ArgumentDefaultsHelpFormatter
26860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    )
26960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    parser.add_argument('--location',
27060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        help = 'the location to download the compiler to',
27160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        default = os.path.join(tempfile.gettempdir(), 'mingw-builds'))
27260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    parser.add_argument('--arch', required = True, choices = ['i686', 'x86_64'],
27360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        help = 'the target MinGW architecture string')
27460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    parser.add_argument('--version', type = str2ver,
27560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        help = 'the version of GCC to download')
27660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    parser.add_argument('--threading', choices = ['posix', 'win32'],
27760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        help = 'the threading type of the compiler')
27860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    parser.add_argument('--exceptions', choices = ['sjlj', 'seh', 'dwarf'],
27960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        help = 'the method to throw exceptions')
28060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    parser.add_argument('--revision', type=int,
28160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        help = 'the revision of the MinGW release')
28260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    group = parser.add_mutually_exclusive_group()
28360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    group.add_argument('-v', '--verbose', action='store_true',
28460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        help='increase the script output verbosity')
28560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    group.add_argument('-q', '--quiet', action='store_true',
28660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        help='only print errors and warning')
28760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    args = parser.parse_args()
28860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
28960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    # Create the logger
29060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    logger = logging.getLogger('mingw')
29160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    handler = logging.StreamHandler()
29260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    formatter = logging.Formatter('%(message)s')
29360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    handler.setFormatter(formatter)
29460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    logger.addHandler(handler)
29560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    logger.setLevel(logging.INFO)
29660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if args.quiet:
29760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        logger.setLevel(logging.WARN)
29860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    if args.verbose:
29960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        logger.setLevel(logging.DEBUG)
30060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
30160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    # Get MinGW
30260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    root_dir = root(location = args.location, arch = args.arch,
30360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        version = args.version, threading = args.threading,
30460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        exceptions = args.exceptions, revision = args.revision,
30560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        log = logger)
30660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
30760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    sys.stdout.write('%s\n' % os.path.join(root_dir, 'bin'))
30860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson
30960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarksonif __name__ == '__main__':
31060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    try:
31160910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        main()
31260910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    except IOError as e:
31360910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        sys.stderr.write('IO error: %s\n' % e)
31460910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        sys.exit(1)
31560910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    except OSError as e:
31660910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        sys.stderr.write('OS error: %s\n' % e)
31760910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        sys.exit(1)
31860910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson    except KeyboardInterrupt as e:
31960910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        sys.stderr.write('Killed\n')
32060910b3de7aa85ff28da14f34c7084711f99ac74Matt Clarkson        sys.exit(1)
321