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", ""))
16if __name__ == "__main__":
17 cppflags.extend(sys.argv[1:])
18cppflags.extend(["-I" + incdir, "-I" + srcdir, "-I" + bindir])
19
20ffi.set_source("mgba._pylib", """
21#define static
22#define inline
23#include "flags.h"
24#define OPAQUE_THREADING
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/ds/ds.h>
38#include <mgba/internal/ds/input.h>
39#include <mgba/internal/gba/gba.h>
40#include <mgba/internal/gba/input.h>
41#include <mgba/internal/gba/renderers/cache-set.h>
42#include <mgba/internal/lr35902/lr35902.h>
43#include <mgba/internal/gb/gb.h>
44#include <mgba/internal/gb/renderers/cache-set.h>
45#include <mgba-util/png-io.h>
46#include <mgba-util/vfs.h>
47
48#define PYEXPORT
49#include "platform/python/core.h"
50#include "platform/python/log.h"
51#include "platform/python/sio.h"
52#include "platform/python/vfs-py.h"
53#undef PYEXPORT
54""", include_dirs=[incdir, srcdir],
55 extra_compile_args=cppflags,
56 libraries=["medusa-emu"],
57 library_dirs=[bindir],
58 runtime_library_dirs=[libdir],
59 sources=[os.path.join(pydir, path) for path in ["vfs-py.c", "core.c", "log.c", "sio.c"]])
60
61preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "_builder.h")], universal_newlines=True)
62
63lines = []
64for line in preprocessed.splitlines():
65 line = line.strip()
66 if line.startswith('#'):
67 continue
68 lines.append(line)
69ffi.cdef('\n'.join(lines))
70
71preprocessed = subprocess.check_output(cpp + ["-fno-inline", "-P"] + cppflags + [os.path.join(pydir, "lib.h")], universal_newlines=True)
72
73lines = []
74for line in preprocessed.splitlines():
75 line = line.strip()
76 if line.startswith('#'):
77 continue
78 lines.append(line)
79ffi.embedding_api('\n'.join(lines))
80
81ffi.embedding_init_code("""
82 import os, os.path
83 venv = os.getenv('VIRTUAL_ENV')
84 if venv:
85 activate = os.path.join(venv, 'bin', 'activate_this.py')
86 exec(compile(open(activate, "rb").read(), activate, 'exec'), dict(__file__=activate))
87 from mgba._pylib import ffi, lib
88 symbols = {}
89 globalSyms = {
90 'symbols': symbols
91 }
92 pendingCode = []
93
94 @ffi.def_extern()
95 def mPythonSetDebugger(debugger):
96 from mgba.debugger import NativeDebugger, CLIDebugger
97 oldDebugger = globalSyms.get('debugger')
98 if oldDebugger and oldDebugger._native == debugger:
99 return
100 if oldDebugger and not debugger:
101 del globalSyms['debugger']
102 return
103 if debugger.type == lib.DEBUGGER_CLI:
104 debugger = CLIDebugger(debugger)
105 else:
106 debugger = NativeDebugger(debugger)
107 globalSyms['debugger'] = debugger
108
109 @ffi.def_extern()
110 def mPythonLoadScript(name, vf):
111 from mgba.vfs import VFile
112 vf = VFile(vf)
113 name = ffi.string(name)
114 source = vf.readAll().decode('utf-8')
115 try:
116 code = compile(source, name, 'exec')
117 pendingCode.append(code)
118 except:
119 return False
120 return True
121
122 @ffi.def_extern()
123 def mPythonRunPending():
124 global pendingCode
125 for code in pendingCode:
126 exec(code, globalSyms, {})
127 pendingCode = []
128
129 @ffi.def_extern()
130 def mPythonDebuggerEntered(reason, info):
131 debugger = globalSyms['debugger']
132 if not debugger:
133 return
134 if info == ffi.NULL:
135 info = None
136 for cb in debugger._cbs:
137 cb(reason, info)
138
139 @ffi.def_extern()
140 def mPythonLookupSymbol(name, outptr):
141 name = ffi.string(name).decode('utf-8')
142 if name not in symbols:
143 return False
144 sym = symbols[name]
145 val = None
146 try:
147 val = int(sym)
148 except:
149 try:
150 val = sym()
151 except:
152 pass
153 if val is None:
154 return False
155 try:
156 outptr[0] = ffi.cast('int32_t', val)
157 return True
158 except:
159 return False
160""")
161
162if __name__ == "__main__":
163 ffi.emit_c_code("lib.c")