src/platform/python/_builder.py (view raw)
1import cffi
2import os, os.path
3import shlex
4import subprocess
5import sys
6
7ffi = cffi.FFI()
8pydir = os.path.dirname(os.path.abspath(__file__))
9srcdir = os.path.join(pydir, "..", "..")
10incdir = os.path.join(pydir, "..", "..", "..", "include")
11bindir = os.environ.get("BINDIR", os.path.join(os.getcwd(), ".."))
12libdir = os.environ.get("LIBDIR")
13
14cpp = shlex.split(os.environ.get("CPP", "cc -E"))
15cppflags = shlex.split(os.environ.get("CPPFLAGS", ""))
16cppflags.extend(["-I" + incdir, "-I" + srcdir, "-I" + bindir])
17
18ffi.set_source("mgba._pylib", """
19#define static
20#define inline
21#define MGBA_EXPORT
22#include <mgba/flags.h>
23#define OPAQUE_THREADING
24#include <mgba/core/blip_buf.h>
25#include <mgba/core/cache-set.h>
26#include <mgba-util/common.h>
27#include <mgba/core/core.h>
28#include <mgba/core/map-cache.h>
29#include <mgba/core/log.h>
30#include <mgba/core/mem-search.h>
31#include <mgba/core/thread.h>
32#include <mgba/core/version.h>
33#include <mgba/debugger/debugger.h>
34#include <mgba/gba/interface.h>
35#include <mgba/internal/arm/arm.h>
36#include <mgba/internal/debugger/cli-debugger.h>
37#include <mgba/internal/gba/gba.h>
38#include <mgba/internal/gba/input.h>
39#include <mgba/internal/gba/renderers/cache-set.h>
40#include <mgba/internal/sm83/sm83.h>
41#include <mgba/internal/gb/gb.h>
42#include <mgba/internal/gb/renderers/cache-set.h>
43#include <mgba-util/png-io.h>
44#include <mgba-util/vfs.h>
45
46#define PYEXPORT
47#include "platform/python/core.h"
48#include "platform/python/log.h"
49#include "platform/python/sio.h"
50#include "platform/python/vfs-py.h"
51#undef PYEXPORT
52""", include_dirs=[incdir, srcdir],
53 extra_compile_args=cppflags,
54 libraries=["mgba"],
55 library_dirs=[bindir],
56 runtime_library_dirs=[libdir],
57 sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "core.c", "log.c", "sio.c"]])
58
59preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True)
60
61lines = []
62for line in preprocessed.splitlines():
63 line = line.strip()
64 if line.startswith('#'):
65 continue
66 lines.append(line)
67ffi.cdef('\n'.join(lines))
68
69ffi.cdef("""
70struct GBARTC {
71 int32_t bytesRemaining;
72 int32_t transferStep;
73 int32_t bitsRead;
74 int32_t bits;
75 int32_t commandActive;
76 RTCCommandData command;
77 RTCControl control;
78 uint8_t time[7];
79};""", packed=True)
80
81preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "lib.h")], universal_newlines=True)
82
83lines = []
84for line in preprocessed.splitlines():
85 line = line.strip()
86 if line.startswith('#'):
87 continue
88 lines.append(line)
89ffi.embedding_api('\n'.join(lines))
90
91ffi.embedding_init_code("""
92 import os, os.path
93 from mgba._pylib import ffi, lib
94 symbols = {}
95 globalSyms = {
96 'symbols': symbols
97 }
98 pendingCode = []
99
100 @ffi.def_extern()
101 def mPythonSetDebugger(debugger):
102 from mgba.debugger import NativeDebugger, CLIDebugger
103 oldDebugger = globalSyms.get('debugger')
104 if oldDebugger and oldDebugger._native == debugger:
105 return
106 if oldDebugger and not debugger:
107 del globalSyms['debugger']
108 return
109 if debugger.type == lib.DEBUGGER_CLI:
110 debugger = CLIDebugger(debugger)
111 else:
112 debugger = NativeDebugger(debugger)
113 globalSyms['debugger'] = debugger
114
115 @ffi.def_extern()
116 def mPythonLoadScript(name, vf):
117 from mgba.vfs import VFile
118 vf = VFile(vf)
119 name = ffi.string(name)
120 source = vf.read_all().decode('utf-8')
121 try:
122 code = compile(source, name, 'exec')
123 pendingCode.append(code)
124 except:
125 return False
126 return True
127
128 @ffi.def_extern()
129 def mPythonRunPending():
130 global pendingCode
131 for code in pendingCode:
132 exec(code, globalSyms, {})
133 pendingCode = []
134
135 @ffi.def_extern()
136 def mPythonDebuggerEntered(reason, info):
137 debugger = globalSyms['debugger']
138 if not debugger:
139 return
140 if info == ffi.NULL:
141 info = None
142 for cb in debugger._cbs:
143 cb(reason, info)
144
145 @ffi.def_extern()
146 def mPythonLookupSymbol(name, outptr):
147 name = ffi.string(name).decode('utf-8')
148 if name not in symbols:
149 return False
150 sym = symbols[name]
151 val = None
152 try:
153 val = int(sym)
154 except:
155 try:
156 val = sym()
157 except:
158 pass
159 if val is None:
160 return False
161 try:
162 outptr[0] = ffi.cast('int32_t', val)
163 return True
164 except:
165 return False
166""")
167
168if __name__ == "__main__":
169 ffi.emit_c_code("lib.c")