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