1#!/usr/bin/env python
2import subprocess
3import re
4import os
5import errno
6import collections
7import sys
8
9class Platform(object):
10    pass
11
12sdk_re = re.compile(r'.*-sdk ([a-zA-Z0-9.]*)')
13
14def sdkinfo(sdkname):
15    ret = {}
16    for line in subprocess.Popen(['xcodebuild', '-sdk', sdkname, '-version'], stdout=subprocess.PIPE).stdout:
17        kv = line.strip().split(': ', 1)
18        if len(kv) == 2:
19            k,v = kv
20            ret[k] = v
21    return ret
22
23desktop_sdk_info = sdkinfo('macosx')
24
25def latest_sdks():
26    latest_desktop = None
27    for line in subprocess.Popen(['xcodebuild', '-showsdks'], stdout=subprocess.PIPE).stdout:
28        match = sdk_re.match(line)
29        if match:
30            if 'OS X' in line:
31                latest_desktop = match.group(1)
32
33    return latest_desktop
34
35desktop_sdk = latest_sdks()
36
37class desktop_platform_32(Platform):
38    sdk='macosx'
39    arch = 'i386'
40    name = 'mac32'
41    triple = 'i386-apple-darwin10'
42    sdkroot = desktop_sdk_info['Path']
43
44    prefix = "#if defined(__i386__) && !defined(__x86_64__)\n\n"
45    suffix = "\n\n#endif"
46
47class desktop_platform_64(Platform):
48    sdk='macosx'
49    arch = 'x86_64'
50    name = 'mac'
51    triple = 'x86_64-apple-darwin10'
52    sdkroot = desktop_sdk_info['Path']
53
54    prefix = "#if !defined(__i386__) && defined(__x86_64__)\n\n"
55    suffix = "\n\n#endif"
56
57def move_file(src_dir, dst_dir, filename, file_suffix=None, prefix='', suffix=''):
58    if not os.path.exists(dst_dir):
59        os.makedirs(dst_dir)
60
61    out_filename = filename
62
63    if file_suffix:
64        split_name = os.path.splitext(filename)
65        out_filename =  "%s_%s%s" % (split_name[0], file_suffix, split_name[1])
66
67    with open(os.path.join(src_dir, filename)) as in_file:
68        with open(os.path.join(dst_dir, out_filename), 'w') as out_file:
69            if prefix:
70                out_file.write(prefix)
71
72            out_file.write(in_file.read())
73
74            if suffix:
75                out_file.write(suffix)
76
77headers_seen = collections.defaultdict(set)
78
79def move_source_tree(src_dir, dest_dir, dest_include_dir, arch=None, prefix=None, suffix=None):
80    for root, dirs, files in os.walk(src_dir, followlinks=True):
81        relroot = os.path.relpath(root,src_dir)
82
83        def move_dir(arch, prefix='', suffix='', files=[]):
84            for file in files:
85                file_suffix = None
86                if file.endswith('.h'):
87                    if dest_include_dir:
88                        file_suffix = arch
89                        if arch:
90                            headers_seen[file].add(arch)
91                        move_file(root, dest_include_dir, file, arch, prefix=prefix, suffix=suffix)
92
93                elif dest_dir:
94                    outroot = os.path.join(dest_dir, relroot)
95                    move_file(root, outroot, file, prefix=prefix, suffix=suffix)
96
97        if relroot == '.':
98            move_dir(arch=arch,
99                     files=files,
100                     prefix=prefix,
101                     suffix=suffix)
102        elif relroot == 'x86':
103            move_dir(arch='i386',
104                     prefix="#if defined(__i386__) && !defined(__x86_64__)\n\n",
105                     suffix="\n\n#endif",
106                     files=files)
107            move_dir(arch='x86_64',
108                     prefix="#if !defined(__i386__) && defined(__x86_64__)\n\n",
109                     suffix="\n\n#endif",
110                     files=files)
111
112def build_target(platform):
113    def xcrun_cmd(cmd):
114        return subprocess.check_output(['xcrun', '-sdk', platform.sdkroot, '-find', cmd]).strip()
115
116    build_dir = 'build_' + platform.name
117    if not os.path.exists(build_dir):
118        os.makedirs(build_dir)
119        env = dict(CC=xcrun_cmd('clang'),
120                   LD=xcrun_cmd('ld'),
121                   CFLAGS='-arch %s -isysroot %s -mmacosx-version-min=10.6' % (platform.arch, platform.sdkroot))
122        working_dir=os.getcwd()
123        try:
124            os.chdir(build_dir)
125            subprocess.check_call(['../configure', '-host', platform.triple], env=env)
126            move_source_tree('.', None, '../osx/include',
127                             arch=platform.arch,
128                             prefix=platform.prefix,
129                             suffix=platform.suffix)
130            move_source_tree('./include', None, '../osx/include',
131                             arch=platform.arch,
132                             prefix=platform.prefix,
133                             suffix=platform.suffix)
134        finally:
135            os.chdir(working_dir)
136
137        for header_name, archs in headers_seen.iteritems():
138            basename, suffix = os.path.splitext(header_name)
139
140def main():
141    move_source_tree('src', 'osx/src', 'osx/include')
142    move_source_tree('include', None, 'osx/include')
143    build_target(desktop_platform_32)
144    build_target(desktop_platform_64)
145
146    for header_name, archs in headers_seen.iteritems():
147        basename, suffix = os.path.splitext(header_name)
148        with open(os.path.join('osx/include', header_name), 'w') as header:
149            for arch in archs:
150                header.write('#include <%s_%s%s>\n' % (basename, arch, suffix))
151
152if __name__ == '__main__':
153    main()
154