all repos — mgba @ 63d7927b60565c68fc0913d4b67f2c5813027f25

mGBA Game Boy Advance Emulator

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")