Completed
Push — master ( 8f28b9...54dd4d )
by Kyle
01:14
created

get_ast()   A

Complexity

Conditions 2

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
dl 0
loc 22
rs 9.2
c 1
b 0
f 0
1
#!/usr/bin/env python3
2
3
import os
4
import sys
5
6
from cffi import FFI
7
import subprocess
8
import platform
9
from pycparser import c_parser, c_ast, parse_file, c_generator
10
11
BITSIZE, LINKAGE = platform.architecture()
12
13
def walk_sources(directory):
14
    for path, dirs, files in os.walk(directory):
15
        for source in files:
16
            if not source.endswith('.c'):
17
                continue
18
            yield os.path.join(path, source)
19
20
def find_sources(directory):
21
    return [os.path.join(directory, source)
22
            for source in os.listdir(directory)
23
            if source.endswith('.c')]
24
25
module_name = 'tcod._libtcod'
26
include_dirs = [
27
                'tcod/',
28
                'libtcod/include/',
29
                'libtcod/src/png/',
30
                'libtcod/src/zlib/',
31
                '/usr/include/SDL2/',
32
                ]
33
34
extra_parse_args = []
35
extra_compile_args = []
36
extra_link_args = []
37
sources = []
38
39
sources += [file for file in walk_sources('libtcod/src')
40
            if 'sys_sfml_c' not in file
41
            and 'sdl12' not in file
42
            ]
43
44
libraries = []
45
library_dirs = []
46
define_macros = [('LIBTCOD_EXPORTS', None),
47
                 ('TCOD_SDL2', None),
48
                 ('NO_OPENGL', None),
49
                 ('TCOD_NO_MACOSX_SDL_MAIN', None),
50
                 ('_CRT_SECURE_NO_WARNINGS', None),
51
                 ]
52
53
sources += find_sources('tcod/')
54
55
if sys.platform == 'win32':
56
    libraries += ['User32', 'OpenGL32']
57
58
if 'linux' in sys.platform:
59
    libraries += ['GL']
60
61
if sys.platform == 'darwin':
62
    extra_link_args += ['-framework', 'OpenGL']
63
64
libraries += ['SDL2']
65
66
# included SDL headers are for whatever OS's don't easily come with them
67
68
if sys.platform in ['win32', 'darwin']:
69
    include_dirs += ['dependencies/SDL2-2.0.4/include']
70
71
    if BITSIZE == '32bit':
72
        library_dirs += [os.path.realpath('dependencies/SDL2-2.0.4/lib/x86')]
73
    else:
74
        library_dirs += [os.path.realpath('dependencies/SDL2-2.0.4/lib/x64')]
75
76
if sys.platform in ['win32', 'darwin']:
77
    include_dirs += ['libtcod/src/zlib/']
78
79
if sys.platform != 'win32':
80
    extra_parse_args += subprocess.check_output(['sdl2-config', '--cflags'],
81
                                              universal_newlines=True
82
                                              ).strip().split()
83
    extra_compile_args += extra_parse_args
84
    extra_link_args += subprocess.check_output(['sdl2-config', '--libs'],
85
                                               universal_newlines=True
86
                                               ).strip().split()
87
88
class CustomPostParser(c_ast.NodeVisitor):
89
90
    def __init__(self):
91
        self.ast = None
92
        self.typedefs = None
93
94
    def parse(self, ast):
95
        self.ast = ast
96
        self.typedefs = []
97
        self.visit(ast)
98
        return ast
99
100
    def visit_Typedef(self, node):
101
        start_node = node
102
        if node.name in ['wchar_t', 'size_t']:
103
            # remove fake typedef placeholders
104
            self.ast.ext.remove(node)
105
        else:
106
            self.generic_visit(node)
107
            if node.name in self.typedefs:
108
                print('warning: %s redefined' % node.name)
109
                self.ast.ext.remove(node)
110
            self.typedefs.append(node.name)
111
112
    def visit_EnumeratorList(self, node):
113
        """Replace enumerator expressions with '...' stubs."""
114
        for type, enum in node.children():
115
            if enum.value is None:
116
                pass
117
            elif isinstance(enum.value, (c_ast.BinaryOp, c_ast.UnaryOp)):
118
                enum.value = c_ast.Constant('int', '...')
119
            elif hasattr(enum.value, 'type'):
120
                enum.value = c_ast.Constant(enum.value.type, '...')
121
122
    def visit_Decl(self, node):
123
        if node.name is None:
124
            self.generic_visit(node)
125
        elif (node.name and 'vsprint' in node.name or
126
              node.name in ['SDL_vsscanf',
127
                            'SDL_vsnprintf',
128
                            'SDL_LogMessageV']):
129
            # exclude va_list related functions
130
            self.ast.ext.remove(node)
131
        elif node.name in ['screen']:
132
            # exclude outdated 'extern SDL_Surface* screen;' line
133
            self.ast.ext.remove(node)
134
        else:
135
            self.generic_visit(node)
136
137
    def visit_FuncDef(self, node):
138
        """Exclude function definitions.  Should be declarations only."""
139
        self.ast.ext.remove(node)
140
141
def get_cdef():
142
    generator = c_generator.CGenerator()
143
    return generator.visit(get_ast())
144
145
def get_ast():
146
    global extra_parse_args
147
    if 'win32' in sys.platform:
148
        extra_parse_args += [r'-Idependencies/SDL2-2.0.4/include']
149
    ast = parse_file(filename='tcod/tcod.h', use_cpp=True,
150
                     cpp_args=[r'-Idependencies/fake_libc_include',
151
                               r'-Ilibtcod/include',
152
                               r'-DDECLSPEC=',
153
                               r'-DSDLCALL=',
154
                               r'-DTCODLIB_API=',
155
                               r'-DTCOD_NO_MACOSX_SDL_MAIN=',
156
                               r'-DTCOD_SDL2=',
157
                               r'-DNO_OPENGL',
158
                               r'-DSDL_FORCE_INLINE=',
159
                               r'-U__GNUC__',
160
                               r'-D_SDL_assert_h',
161
                               r'-D_SDL_thread_h',
162
                               r'-DDOXYGEN_SHOULD_IGNORE_THIS',
163
                               r'-DMAC_OS_X_VERSION_MIN_REQUIRED=9999',
164
                               ] + extra_parse_args)
165
    ast = CustomPostParser().parse(ast)
166
    return ast
167
168
ffi = FFI()
169
ffi.cdef(get_cdef())
170
ffi.cdef('''
171
extern "Python" {
172
    static bool pycall_parser_new_struct(TCOD_parser_struct_t str,const char *name);
173
    static bool pycall_parser_new_flag(const char *name);
174
    static bool pycall_parser_new_property(const char *propname, TCOD_value_type_t type, TCOD_value_t value);
175
    static bool pycall_parser_end_struct(TCOD_parser_struct_t str, const char *name);
176
    static void pycall_parser_error(const char *msg);
177
178
    static bool _pycall_bsp_callback(TCOD_bsp_t *node, void *userData);
179
180
    static float _pycall_path_func( int xFrom, int yFrom, int xTo, int yTo, void *user_data );
181
182
    static bool _pycall_line_listener(int x, int y);
183
184
    static void _pycall_sdl_hook(void *);
185
}
186
''')
187
ffi.set_source(
188
    module_name, '#include <tcod.h>',
189
    include_dirs=include_dirs,
190
    library_dirs=library_dirs,
191
    sources=sources,
192
    libraries=libraries,
193
    extra_compile_args=extra_compile_args,
194
    extra_link_args=extra_link_args,
195
    define_macros=define_macros,
196
)
197
198
if __name__ == "__main__":
199
    ffi.compile()
200