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