xref: /netbsd-src/external/apache2/llvm/dist/libcxx/utils/google-benchmark/mingw.py (revision 4d6fc14bc9b0c5bf3e30be318c143ee82cadd108)
1*4d6fc14bSjoerg#!/usr/bin/env python
2*4d6fc14bSjoerg# encoding: utf-8
3*4d6fc14bSjoerg
4*4d6fc14bSjoergimport argparse
5*4d6fc14bSjoergimport errno
6*4d6fc14bSjoergimport logging
7*4d6fc14bSjoergimport os
8*4d6fc14bSjoergimport platform
9*4d6fc14bSjoergimport re
10*4d6fc14bSjoergimport sys
11*4d6fc14bSjoergimport subprocess
12*4d6fc14bSjoergimport tempfile
13*4d6fc14bSjoerg
14*4d6fc14bSjoergtry:
15*4d6fc14bSjoerg    import winreg
16*4d6fc14bSjoergexcept ImportError:
17*4d6fc14bSjoerg    import _winreg as winreg
18*4d6fc14bSjoergtry:
19*4d6fc14bSjoerg    import urllib.request as request
20*4d6fc14bSjoergexcept ImportError:
21*4d6fc14bSjoerg    import urllib as request
22*4d6fc14bSjoergtry:
23*4d6fc14bSjoerg    import urllib.parse as parse
24*4d6fc14bSjoergexcept ImportError:
25*4d6fc14bSjoerg    import urlparse as parse
26*4d6fc14bSjoerg
27*4d6fc14bSjoergclass EmptyLogger(object):
28*4d6fc14bSjoerg    '''
29*4d6fc14bSjoerg    Provides an implementation that performs no logging
30*4d6fc14bSjoerg    '''
31*4d6fc14bSjoerg    def debug(self, *k, **kw):
32*4d6fc14bSjoerg        pass
33*4d6fc14bSjoerg    def info(self, *k, **kw):
34*4d6fc14bSjoerg        pass
35*4d6fc14bSjoerg    def warn(self, *k, **kw):
36*4d6fc14bSjoerg        pass
37*4d6fc14bSjoerg    def error(self, *k, **kw):
38*4d6fc14bSjoerg        pass
39*4d6fc14bSjoerg    def critical(self, *k, **kw):
40*4d6fc14bSjoerg        pass
41*4d6fc14bSjoerg    def setLevel(self, *k, **kw):
42*4d6fc14bSjoerg        pass
43*4d6fc14bSjoerg
44*4d6fc14bSjoergurls = (
45*4d6fc14bSjoerg    'http://downloads.sourceforge.net/project/mingw-w64/Toolchains%20'
46*4d6fc14bSjoerg        'targetting%20Win32/Personal%20Builds/mingw-builds/installer/'
47*4d6fc14bSjoerg        'repository.txt',
48*4d6fc14bSjoerg    'http://downloads.sourceforge.net/project/mingwbuilds/host-windows/'
49*4d6fc14bSjoerg        'repository.txt'
50*4d6fc14bSjoerg)
51*4d6fc14bSjoerg'''
52*4d6fc14bSjoergA list of mingw-build repositories
53*4d6fc14bSjoerg'''
54*4d6fc14bSjoerg
55*4d6fc14bSjoergdef repository(urls = urls, log = EmptyLogger()):
56*4d6fc14bSjoerg    '''
57*4d6fc14bSjoerg    Downloads and parse mingw-build repository files and parses them
58*4d6fc14bSjoerg    '''
59*4d6fc14bSjoerg    log.info('getting mingw-builds repository')
60*4d6fc14bSjoerg    versions = {}
61*4d6fc14bSjoerg    re_sourceforge = re.compile(r'http://sourceforge.net/projects/([^/]+)/files')
62*4d6fc14bSjoerg    re_sub = r'http://downloads.sourceforge.net/project/\1'
63*4d6fc14bSjoerg    for url in urls:
64*4d6fc14bSjoerg        log.debug(' - requesting: %s', url)
65*4d6fc14bSjoerg        socket = request.urlopen(url)
66*4d6fc14bSjoerg        repo = socket.read()
67*4d6fc14bSjoerg        if not isinstance(repo, str):
68*4d6fc14bSjoerg            repo = repo.decode();
69*4d6fc14bSjoerg        socket.close()
70*4d6fc14bSjoerg        for entry in repo.split('\n')[:-1]:
71*4d6fc14bSjoerg            value = entry.split('|')
72*4d6fc14bSjoerg            version = tuple([int(n) for n in value[0].strip().split('.')])
73*4d6fc14bSjoerg            version = versions.setdefault(version, {})
74*4d6fc14bSjoerg            arch = value[1].strip()
75*4d6fc14bSjoerg            if arch == 'x32':
76*4d6fc14bSjoerg                arch = 'i686'
77*4d6fc14bSjoerg            elif arch == 'x64':
78*4d6fc14bSjoerg                arch = 'x86_64'
79*4d6fc14bSjoerg            arch = version.setdefault(arch, {})
80*4d6fc14bSjoerg            threading = arch.setdefault(value[2].strip(), {})
81*4d6fc14bSjoerg            exceptions = threading.setdefault(value[3].strip(), {})
82*4d6fc14bSjoerg            revision = exceptions.setdefault(int(value[4].strip()[3:]),
83*4d6fc14bSjoerg                re_sourceforge.sub(re_sub, value[5].strip()))
84*4d6fc14bSjoerg    return versions
85*4d6fc14bSjoerg
86*4d6fc14bSjoergdef find_in_path(file, path=None):
87*4d6fc14bSjoerg    '''
88*4d6fc14bSjoerg    Attempts to find an executable in the path
89*4d6fc14bSjoerg    '''
90*4d6fc14bSjoerg    if platform.system() == 'Windows':
91*4d6fc14bSjoerg        file += '.exe'
92*4d6fc14bSjoerg    if path is None:
93*4d6fc14bSjoerg        path = os.environ.get('PATH', '')
94*4d6fc14bSjoerg    if type(path) is type(''):
95*4d6fc14bSjoerg        path = path.split(os.pathsep)
96*4d6fc14bSjoerg    return list(filter(os.path.exists,
97*4d6fc14bSjoerg        map(lambda dir, file=file: os.path.join(dir, file), path)))
98*4d6fc14bSjoerg
99*4d6fc14bSjoergdef find_7zip(log = EmptyLogger()):
100*4d6fc14bSjoerg    '''
101*4d6fc14bSjoerg    Attempts to find 7zip for unpacking the mingw-build archives
102*4d6fc14bSjoerg    '''
103*4d6fc14bSjoerg    log.info('finding 7zip')
104*4d6fc14bSjoerg    path = find_in_path('7z')
105*4d6fc14bSjoerg    if not path:
106*4d6fc14bSjoerg        key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\7-Zip')
107*4d6fc14bSjoerg        path, _ = winreg.QueryValueEx(key, 'Path')
108*4d6fc14bSjoerg        path = [os.path.join(path, '7z.exe')]
109*4d6fc14bSjoerg    log.debug('found \'%s\'', path[0])
110*4d6fc14bSjoerg    return path[0]
111*4d6fc14bSjoerg
112*4d6fc14bSjoergfind_7zip()
113*4d6fc14bSjoerg
114*4d6fc14bSjoergdef unpack(archive, location, log = EmptyLogger()):
115*4d6fc14bSjoerg    '''
116*4d6fc14bSjoerg    Unpacks a mingw-builds archive
117*4d6fc14bSjoerg    '''
118*4d6fc14bSjoerg    sevenzip = find_7zip(log)
119*4d6fc14bSjoerg    log.info('unpacking %s', os.path.basename(archive))
120*4d6fc14bSjoerg    cmd = [sevenzip, 'x', archive, '-o' + location, '-y']
121*4d6fc14bSjoerg    log.debug(' - %r', cmd)
122*4d6fc14bSjoerg    with open(os.devnull, 'w') as devnull:
123*4d6fc14bSjoerg        subprocess.check_call(cmd, stdout = devnull)
124*4d6fc14bSjoerg
125*4d6fc14bSjoergdef download(url, location, log = EmptyLogger()):
126*4d6fc14bSjoerg    '''
127*4d6fc14bSjoerg    Downloads and unpacks a mingw-builds archive
128*4d6fc14bSjoerg    '''
129*4d6fc14bSjoerg    log.info('downloading MinGW')
130*4d6fc14bSjoerg    log.debug(' - url: %s', url)
131*4d6fc14bSjoerg    log.debug(' - location: %s', location)
132*4d6fc14bSjoerg
133*4d6fc14bSjoerg    re_content = re.compile(r'attachment;[ \t]*filename=(")?([^"]*)(")?[\r\n]*')
134*4d6fc14bSjoerg
135*4d6fc14bSjoerg    stream = request.urlopen(url)
136*4d6fc14bSjoerg    try:
137*4d6fc14bSjoerg        content = stream.getheader('Content-Disposition') or ''
138*4d6fc14bSjoerg    except AttributeError:
139*4d6fc14bSjoerg        content = stream.headers.getheader('Content-Disposition') or ''
140*4d6fc14bSjoerg    matches = re_content.match(content)
141*4d6fc14bSjoerg    if matches:
142*4d6fc14bSjoerg        filename = matches.group(2)
143*4d6fc14bSjoerg    else:
144*4d6fc14bSjoerg        parsed = parse.urlparse(stream.geturl())
145*4d6fc14bSjoerg        filename = os.path.basename(parsed.path)
146*4d6fc14bSjoerg
147*4d6fc14bSjoerg    try:
148*4d6fc14bSjoerg        os.makedirs(location)
149*4d6fc14bSjoerg    except OSError as e:
150*4d6fc14bSjoerg        if e.errno == errno.EEXIST and os.path.isdir(location):
151*4d6fc14bSjoerg            pass
152*4d6fc14bSjoerg        else:
153*4d6fc14bSjoerg            raise
154*4d6fc14bSjoerg
155*4d6fc14bSjoerg    archive = os.path.join(location, filename)
156*4d6fc14bSjoerg    with open(archive, 'wb') as out:
157*4d6fc14bSjoerg        while True:
158*4d6fc14bSjoerg            buf = stream.read(1024)
159*4d6fc14bSjoerg            if not buf:
160*4d6fc14bSjoerg                break
161*4d6fc14bSjoerg            out.write(buf)
162*4d6fc14bSjoerg    unpack(archive, location, log = log)
163*4d6fc14bSjoerg    os.remove(archive)
164*4d6fc14bSjoerg
165*4d6fc14bSjoerg    possible = os.path.join(location, 'mingw64')
166*4d6fc14bSjoerg    if not os.path.exists(possible):
167*4d6fc14bSjoerg        possible = os.path.join(location, 'mingw32')
168*4d6fc14bSjoerg        if not os.path.exists(possible):
169*4d6fc14bSjoerg            raise ValueError('Failed to find unpacked MinGW: ' + possible)
170*4d6fc14bSjoerg    return possible
171*4d6fc14bSjoerg
172*4d6fc14bSjoergdef root(location = None, arch = None, version = None, threading = None,
173*4d6fc14bSjoerg        exceptions = None, revision = None, log = EmptyLogger()):
174*4d6fc14bSjoerg    '''
175*4d6fc14bSjoerg    Returns the root folder of a specific version of the mingw-builds variant
176*4d6fc14bSjoerg    of gcc. Will download the compiler if needed
177*4d6fc14bSjoerg    '''
178*4d6fc14bSjoerg
179*4d6fc14bSjoerg    # Get the repository if we don't have all the information
180*4d6fc14bSjoerg    if not (arch and version and threading and exceptions and revision):
181*4d6fc14bSjoerg        versions = repository(log = log)
182*4d6fc14bSjoerg
183*4d6fc14bSjoerg    # Determine some defaults
184*4d6fc14bSjoerg    version = version or max(versions.keys())
185*4d6fc14bSjoerg    if not arch:
186*4d6fc14bSjoerg        arch = platform.machine().lower()
187*4d6fc14bSjoerg        if arch == 'x86':
188*4d6fc14bSjoerg            arch = 'i686'
189*4d6fc14bSjoerg        elif arch == 'amd64':
190*4d6fc14bSjoerg            arch = 'x86_64'
191*4d6fc14bSjoerg    if not threading:
192*4d6fc14bSjoerg        keys = versions[version][arch].keys()
193*4d6fc14bSjoerg        if 'posix' in keys:
194*4d6fc14bSjoerg            threading = 'posix'
195*4d6fc14bSjoerg        elif 'win32' in keys:
196*4d6fc14bSjoerg            threading = 'win32'
197*4d6fc14bSjoerg        else:
198*4d6fc14bSjoerg            threading = keys[0]
199*4d6fc14bSjoerg    if not exceptions:
200*4d6fc14bSjoerg        keys = versions[version][arch][threading].keys()
201*4d6fc14bSjoerg        if 'seh' in keys:
202*4d6fc14bSjoerg            exceptions = 'seh'
203*4d6fc14bSjoerg        elif 'sjlj' in keys:
204*4d6fc14bSjoerg            exceptions = 'sjlj'
205*4d6fc14bSjoerg        else:
206*4d6fc14bSjoerg            exceptions = keys[0]
207*4d6fc14bSjoerg    if revision == None:
208*4d6fc14bSjoerg        revision = max(versions[version][arch][threading][exceptions].keys())
209*4d6fc14bSjoerg    if not location:
210*4d6fc14bSjoerg        location = os.path.join(tempfile.gettempdir(), 'mingw-builds')
211*4d6fc14bSjoerg
212*4d6fc14bSjoerg    # Get the download url
213*4d6fc14bSjoerg    url = versions[version][arch][threading][exceptions][revision]
214*4d6fc14bSjoerg
215*4d6fc14bSjoerg    # Tell the user whatzzup
216*4d6fc14bSjoerg    log.info('finding MinGW %s', '.'.join(str(v) for v in version))
217*4d6fc14bSjoerg    log.debug(' - arch: %s', arch)
218*4d6fc14bSjoerg    log.debug(' - threading: %s', threading)
219*4d6fc14bSjoerg    log.debug(' - exceptions: %s', exceptions)
220*4d6fc14bSjoerg    log.debug(' - revision: %s', revision)
221*4d6fc14bSjoerg    log.debug(' - url: %s', url)
222*4d6fc14bSjoerg
223*4d6fc14bSjoerg    # Store each specific revision differently
224*4d6fc14bSjoerg    slug = '{version}-{arch}-{threading}-{exceptions}-rev{revision}'
225*4d6fc14bSjoerg    slug = slug.format(
226*4d6fc14bSjoerg        version = '.'.join(str(v) for v in version),
227*4d6fc14bSjoerg        arch = arch,
228*4d6fc14bSjoerg        threading = threading,
229*4d6fc14bSjoerg        exceptions = exceptions,
230*4d6fc14bSjoerg        revision = revision
231*4d6fc14bSjoerg    )
232*4d6fc14bSjoerg    if arch == 'x86_64':
233*4d6fc14bSjoerg        root_dir = os.path.join(location, slug, 'mingw64')
234*4d6fc14bSjoerg    elif arch == 'i686':
235*4d6fc14bSjoerg        root_dir = os.path.join(location, slug, 'mingw32')
236*4d6fc14bSjoerg    else:
237*4d6fc14bSjoerg        raise ValueError('Unknown MinGW arch: ' + arch)
238*4d6fc14bSjoerg
239*4d6fc14bSjoerg    # Download if needed
240*4d6fc14bSjoerg    if not os.path.exists(root_dir):
241*4d6fc14bSjoerg        downloaded = download(url, os.path.join(location, slug), log = log)
242*4d6fc14bSjoerg        if downloaded != root_dir:
243*4d6fc14bSjoerg            raise ValueError('The location of mingw did not match\n%s\n%s'
244*4d6fc14bSjoerg                % (downloaded, root_dir))
245*4d6fc14bSjoerg
246*4d6fc14bSjoerg    return root_dir
247*4d6fc14bSjoerg
248*4d6fc14bSjoergdef str2ver(string):
249*4d6fc14bSjoerg    '''
250*4d6fc14bSjoerg    Converts a version string into a tuple
251*4d6fc14bSjoerg    '''
252*4d6fc14bSjoerg    try:
253*4d6fc14bSjoerg        version = tuple(int(v) for v in string.split('.'))
254*4d6fc14bSjoerg        if len(version) is not 3:
255*4d6fc14bSjoerg            raise ValueError()
256*4d6fc14bSjoerg    except ValueError:
257*4d6fc14bSjoerg        raise argparse.ArgumentTypeError(
258*4d6fc14bSjoerg            'please provide a three digit version string')
259*4d6fc14bSjoerg    return version
260*4d6fc14bSjoerg
261*4d6fc14bSjoergdef main():
262*4d6fc14bSjoerg    '''
263*4d6fc14bSjoerg    Invoked when the script is run directly by the python interpreter
264*4d6fc14bSjoerg    '''
265*4d6fc14bSjoerg    parser = argparse.ArgumentParser(
266*4d6fc14bSjoerg        description = 'Downloads a specific version of MinGW',
267*4d6fc14bSjoerg        formatter_class = argparse.ArgumentDefaultsHelpFormatter
268*4d6fc14bSjoerg    )
269*4d6fc14bSjoerg    parser.add_argument('--location',
270*4d6fc14bSjoerg        help = 'the location to download the compiler to',
271*4d6fc14bSjoerg        default = os.path.join(tempfile.gettempdir(), 'mingw-builds'))
272*4d6fc14bSjoerg    parser.add_argument('--arch', required = True, choices = ['i686', 'x86_64'],
273*4d6fc14bSjoerg        help = 'the target MinGW architecture string')
274*4d6fc14bSjoerg    parser.add_argument('--version', type = str2ver,
275*4d6fc14bSjoerg        help = 'the version of GCC to download')
276*4d6fc14bSjoerg    parser.add_argument('--threading', choices = ['posix', 'win32'],
277*4d6fc14bSjoerg        help = 'the threading type of the compiler')
278*4d6fc14bSjoerg    parser.add_argument('--exceptions', choices = ['sjlj', 'seh', 'dwarf'],
279*4d6fc14bSjoerg        help = 'the method to throw exceptions')
280*4d6fc14bSjoerg    parser.add_argument('--revision', type=int,
281*4d6fc14bSjoerg        help = 'the revision of the MinGW release')
282*4d6fc14bSjoerg    group = parser.add_mutually_exclusive_group()
283*4d6fc14bSjoerg    group.add_argument('-v', '--verbose', action='store_true',
284*4d6fc14bSjoerg        help='increase the script output verbosity')
285*4d6fc14bSjoerg    group.add_argument('-q', '--quiet', action='store_true',
286*4d6fc14bSjoerg        help='only print errors and warning')
287*4d6fc14bSjoerg    args = parser.parse_args()
288*4d6fc14bSjoerg
289*4d6fc14bSjoerg    # Create the logger
290*4d6fc14bSjoerg    logger = logging.getLogger('mingw')
291*4d6fc14bSjoerg    handler = logging.StreamHandler()
292*4d6fc14bSjoerg    formatter = logging.Formatter('%(message)s')
293*4d6fc14bSjoerg    handler.setFormatter(formatter)
294*4d6fc14bSjoerg    logger.addHandler(handler)
295*4d6fc14bSjoerg    logger.setLevel(logging.INFO)
296*4d6fc14bSjoerg    if args.quiet:
297*4d6fc14bSjoerg        logger.setLevel(logging.WARN)
298*4d6fc14bSjoerg    if args.verbose:
299*4d6fc14bSjoerg        logger.setLevel(logging.DEBUG)
300*4d6fc14bSjoerg
301*4d6fc14bSjoerg    # Get MinGW
302*4d6fc14bSjoerg    root_dir = root(location = args.location, arch = args.arch,
303*4d6fc14bSjoerg        version = args.version, threading = args.threading,
304*4d6fc14bSjoerg        exceptions = args.exceptions, revision = args.revision,
305*4d6fc14bSjoerg        log = logger)
306*4d6fc14bSjoerg
307*4d6fc14bSjoerg    sys.stdout.write('%s\n' % os.path.join(root_dir, 'bin'))
308*4d6fc14bSjoerg
309*4d6fc14bSjoergif __name__ == '__main__':
310*4d6fc14bSjoerg    try:
311*4d6fc14bSjoerg        main()
312*4d6fc14bSjoerg    except IOError as e:
313*4d6fc14bSjoerg        sys.stderr.write('IO error: %s\n' % e)
314*4d6fc14bSjoerg        sys.exit(1)
315*4d6fc14bSjoerg    except OSError as e:
316*4d6fc14bSjoerg        sys.stderr.write('OS error: %s\n' % e)
317*4d6fc14bSjoerg        sys.exit(1)
318*4d6fc14bSjoerg    except KeyboardInterrupt as e:
319*4d6fc14bSjoerg        sys.stderr.write('Killed\n')
320*4d6fc14bSjoerg        sys.exit(1)
321