all repos — mgba @ 7bd29d6e44c60a7812c499879e160d3a95e53f25

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