from cffi import FFI ffibuilder = FFI() definitions = ["-DACCOUNT_API=", "-DACCOUNT_NOINCLUDE"] header = Path(__file__).resolve().parent / "account.h" command = ["cc", "-E"] + definitions + [str(header)] interface = check_output(command).decode("utf-8") # remove possible \r characters on windows which # would confuse cdef _interface = [l.strip("\r") for l in interface.split("\n")] # cdef() expects a single string declaring the C types, functions and # globals needed to use the shared object. It must be in valid C syntax. ffibuilder.cdef("\n".join(_interface)) # set_source() gives the name of the python extension module to # produce, and some C source code as a string. This C code needs # to make the declared functions, types and globals available, # so it is often just the "#include". ffibuilder.set_source( "_pyaccount", """ #include "account.h" """, ) ffibuilder.emit_c_code("_pyaccount.c")
ffi = FFI() ffi.set_source( "_flux._security", """ #include <flux/security/sign.h> // TODO: remove this when we can use cffi 1.10 #ifdef __GNUC__ #pragma GCC visibility push(default) #endif """, ) cdefs = """ typedef int... ptrdiff_t; typedef int... pid_t; typedef ... va_list; void free(void *ptr); """ with open("_security_preproc.h") as h: cdefs = cdefs + h.read() ffi.cdef(cdefs) if __name__ == "__main__": ffi.emit_c_code("_security.c")
ffibuilder = FFI() path = os.path.dirname(os.path.abspath(__file__)) cnt = [] with open(os.path.join(path, '..', 'libclingo-dl', 'clingo-dl.h')) as f: for line in f: if not re.match(r' *(#|//|extern *"C" *{|}$|$)', line): cnt.append( re.sub(r'[A-Z_]+_VISIBILITY_DEFAULT ', '', line).strip()) cnt.append( 'extern "Python" bool pyclingodl_rewrite(clingo_ast_t *ast, void *data);') code = '\n'.join(cnt) ffibuilder.cdef(f'''\ typedef uint64_t clingo_symbol_t; typedef struct clingo_ast_statement clingo_ast_statement_t; typedef struct clingo_ast clingo_ast_t; typedef struct clingo_control clingo_control_t; typedef struct clingo_options clingo_options_t; typedef struct clingo_model clingo_model_t; typedef struct clingo_statistics clingo_statistics_t; {code} ''') ffibuilder.set_source("_clingodl", """\ #include "clingo-dl.h" """) ffibuilder.emit_c_code('_clingodl.c')
#include <czmq.h> #include "src/common/librlist/rlist.h" // TODO: remove this when we can use cffi 1.10 #ifdef __GNUC__ #pragma GCC visibility push(default) #endif """, ) cdefs = """ typedef struct _zlistx_t zlistx_t; typedef struct _zhashx_t zhashx_t; typedef int... json_type; typedef struct json_t json_t; typedef struct json_error_t json_error_t; void free (void *); """ with open("_rlist_preproc.h") as h: cdefs = cdefs + h.read() ffi.cdef(cdefs) if __name__ == "__main__": ffi.emit_c_code("_rlist.c") # Ensure target mtime is updated Path("_rlist.c").touch()
def prepare(space, cdef, module_name, source, w_includes=None, w_extra_source=None): try: import cffi from cffi import FFI # <== the system one, which from cffi import recompiler # needs to be at least cffi 1.0.4 from cffi import ffiplatform except ImportError: py.test.skip("system cffi module not found or older than 1.0.0") if cffi.__version_info__ < (1, 2, 0): py.test.skip("system cffi module needs to be at least 1.2.0") space.appexec([], """(): import _cffi_backend # force it to be initialized """) includes = [] if w_includes: includes += space.unpackiterable(w_includes) assert module_name.startswith('test_') module_name = '_CFFI_' + module_name rdir = udir.ensure('recompiler', dir=1) rdir.join('Python.h').write( '#define PYPY_VERSION XX\n' '#define PyMODINIT_FUNC /*exported*/ void\n' ) path = module_name.replace('.', os.sep) if '.' in module_name: subrdir = rdir.join(module_name[:module_name.index('.')]) os.mkdir(str(subrdir)) else: subrdir = rdir c_file = str(rdir.join('%s.c' % path)) ffi = FFI() for include_ffi_object in includes: ffi.include(include_ffi_object._test_recompiler_source_ffi) ffi.cdef(cdef) ffi.set_source(module_name, source) ffi.emit_c_code(c_file) base_module_name = module_name.split('.')[-1] sources = [] if w_extra_source is not None: sources.append(space.str_w(w_extra_source)) ext = ffiplatform.get_extension(c_file, module_name, include_dirs=[str(rdir)], export_symbols=['_cffi_pypyinit_' + base_module_name], sources=sources) ffiplatform.compile(str(rdir), ext) for extension in ['so', 'pyd', 'dylib']: so_file = str(rdir.join('%s.%s' % (path, extension))) if os.path.exists(so_file): break else: raise Exception("could not find the compiled extension module?") args_w = [space.wrap(module_name), space.wrap(so_file)] w_res = space.appexec(args_w, """(modulename, filename): import imp mod = imp.load_dynamic(modulename, filename) assert mod.__name__ == modulename return (mod.ffi, mod.lib) """) ffiobject = space.getitem(w_res, space.wrap(0)) ffiobject._test_recompiler_source_ffi = ffi if not hasattr(space, '_cleanup_ffi'): space._cleanup_ffi = [] space._cleanup_ffi.append(ffiobject) return w_res
# Remove function-like macros declarations = re.sub(r"^#define \w+\(.*$", "", declarations, flags=re.MULTILINE) # Make macro value always be "..." declarations = re.sub(r"^(#define \w+).*$", lambda m: m.group(1) + " ...", declarations, flags=re.MULTILINE) # Mark callbacks with special CFFI syntax if args.callbacks_file: declarations = re.sub(r"^(.*\b" + callbacks_regex + r"\b.*)$", lambda m: 'extern "Python+C" ' + m.group(1), declarations, flags=re.MULTILINE) # Remove empty lines declarations = re.sub(r"^\s*$\n", "", declarations, flags=re.MULTILINE) try: builder = FFI() builder.cdef(declarations) builder.set_source(args.module_name, f'#include "{args.header_file}"') builder.emit_c_code(args.output_file) except Exception as e: for lineno, line in enumerate(declarations.split("\n"), start=1): print(f"{lineno:3d}: {line}") print(e) sys.exit(1)
from cffi import FFI ffi = FFI() ffi.set_source( "_flux._idset", """ #include <flux/idset.h> // TODO: remove this when we can use cffi 1.10 #ifdef __GNUC__ #pragma GCC visibility push(default) #endif """, ) cdefs = """ static const unsigned int IDSET_INVALID_ID; void free (void *); """ with open("_idset_preproc.h") as h: cdefs = cdefs + h.read() ffi.cdef(cdefs) if __name__ == "__main__": ffi.emit_c_code("_idset.c") # Ensure target mtime is updated Path("_idset.c").touch()
void * unpack_long(ptrdiff_t num){ return (void*)num; } // TODO: remove this when we can use cffi 1.10 #ifdef __GNUC__ #pragma GCC visibility push(default) #endif """, libraries=["flux-core"], ) cdefs = """ typedef int... ptrdiff_t; typedef int... pid_t; typedef ... va_list; void * unpack_long(ptrdiff_t num); void free(void *ptr); """ with open("_core_preproc.h") as h: cdefs = cdefs + h.read() ffi.cdef(cdefs) if __name__ == "__main__": ffi.emit_c_code("_core.c") # ensure mtime of target is updated Path("_core.c").touch()
def generate_c(action): clingo_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) ffi = FFI() cnt = [] with open(f'{clingo_dir}/libclingo/clingo.h') as f: for line in f: if not re.match(r' *(#|//|extern *"C" *{|}$|$)', line): cnt.append( line.replace('CLINGO_VISIBILITY_DEFAULT ', '').strip()) # callbacks cnt.append( 'extern "Python" bool pyclingo_solve_event_callback(clingo_solve_event_type_t type, void *event, void *data, bool *goon);' ) cnt.append( 'extern "Python" void pyclingo_logger_callback(clingo_warning_t code, char const *message, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_ground_callback(clingo_location_t const *location, char const *name, clingo_symbol_t const *arguments, size_t arguments_size, void *data, clingo_symbol_callback_t symbol_callback, void *symbol_callback_data);' ) # propagator callbacks cnt.append( 'extern "Python" bool pyclingo_propagator_init(clingo_propagate_init_t *init, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_propagator_propagate(clingo_propagate_control_t *control, clingo_literal_t const *changes, size_t size, void *data);' ) cnt.append( 'extern "Python" void pyclingo_propagator_undo(clingo_propagate_control_t const *control, clingo_literal_t const *changes, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_propagator_check(clingo_propagate_control_t *control, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_propagator_decide(clingo_id_t thread_id, clingo_assignment_t const *assignment, clingo_literal_t fallback, void *data, clingo_literal_t *decision);' ) # observer callbacks cnt.append( 'extern "Python" bool pyclingo_observer_init_program(bool incremental, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_begin_step(void *data);') cnt.append('extern "Python" bool pyclingo_observer_end_step(void *data);') cnt.append( 'extern "Python" bool pyclingo_observer_rule(bool choice, clingo_atom_t const *head, size_t head_size, clingo_literal_t const *body, size_t body_size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_weight_rule(bool choice, clingo_atom_t const *head, size_t head_size, clingo_weight_t lower_bound, clingo_weighted_literal_t const *body, size_t body_size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_minimize(clingo_weight_t priority, clingo_weighted_literal_t const* literals, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_project(clingo_atom_t const *atoms, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_output_atom(clingo_symbol_t symbol, clingo_atom_t atom, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_output_term(clingo_symbol_t symbol, clingo_literal_t const *condition, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_output_csp(clingo_symbol_t symbol, int value, clingo_literal_t const *condition, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_external(clingo_atom_t atom, clingo_external_type_t type, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_assume(clingo_literal_t const *literals, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_heuristic(clingo_atom_t atom, clingo_heuristic_type_t type, int bias, unsigned priority, clingo_literal_t const *condition, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_acyc_edge(int node_u, int node_v, clingo_literal_t const *condition, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_term_number(clingo_id_t term_id, int number, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_term_string(clingo_id_t term_id, char const *name, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_term_compound(clingo_id_t term_id, int name_id_or_type, clingo_id_t const *arguments, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_element(clingo_id_t element_id, clingo_id_t const *terms, size_t terms_size, clingo_literal_t const *condition, size_t condition_size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_atom(clingo_id_t atom_id_or_zero, clingo_id_t term_id, clingo_id_t const *elements, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_atom_with_guard(clingo_id_t atom_id_or_zero, clingo_id_t term_id, clingo_id_t const *elements, size_t size, clingo_id_t operator_id, clingo_id_t right_hand_side_id, void *data);' ) # application callbacks cnt.append( 'extern "Python" char const *pyclingo_application_program_name(void *data);' ) cnt.append( 'extern "Python" char const *pyclingo_application_version(void *data);' ) cnt.append( 'extern "Python" unsigned pyclingo_application_message_limit(void *data);' ) cnt.append( 'extern "Python" bool pyclingo_application_main(clingo_control_t *control, char const *const * files, size_t size, void *data);' ) cnt.append( 'extern "Python" void pyclingo_application_logger(clingo_warning_t code, char const *message, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_application_print_model(clingo_model_t const *model, clingo_default_model_printer_t printer, void *printer_data, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_application_register_options(clingo_options_t *options, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_application_validate_options(void *data);' ) # application options callbacks cnt.append( 'extern "Python" bool pyclingo_application_options_parse(char const *value, void *data);' ) # ast callbacks cnt.append( 'extern "Python" bool pyclingo_ast_callback(clingo_ast_t const *, void *);' ) # script callbacks cnt.append( 'extern "Python" bool pyclingo_script_execute(clingo_location_t *loc, char const *code, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_script_call(clingo_location_t *loc, char const *name, void *arguments, size_t size, void *symbol_callback, void *symbol_callback_data, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_script_callable(char const * name, bool *ret, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_script_main(clingo_control_t *ctl, void *data);' ) code = '' if action == "embed": ffi.embedding_api('''\ bool pyclingo_execute(void *loc, char const *code, void *data); bool pyclingo_call(void *loc, char const *name, void *arguments, size_t size, void *symbol_callback, void *symbol_callback_data, void *data); bool pyclingo_callable(char const * name, bool *ret, void *data); bool pyclingo_main(void *ctl, void *data); ''') ffi.embedding_init_code(f"""\ import os import sys import clingo.script sys.path.insert(0, os.getcwd()) """) code = '''\ #ifdef CFFI_DLLEXPORT #undef CFFI_DLLEXPORT #define CFFI_DLLEXPORT #endif #ifdef PYPY_VERSION void pyclingo_finalize() { } #else void pyclingo_finalize() { if (Py_IsInitialized()) { PyGILState_Ensure(); Py_Finalize(); } } #endif ''' else: cnt.append( 'extern "Python" bool pyclingo_execute(void *loc, char const *code, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_call(void *loc, char const *name, void *arguments, size_t size, void *symbol_callback, void *symbol_callback_data, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_callable(char const * name, bool *ret, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_main(void *ctl, void *data);') if action != "header": ffi.set_source('_clingo', f'''\ #include <clingo.h> {code} ''') ffi.cdef('\n'.join(cnt)) ffi.emit_c_code('_clingo.c') else: with open('_clingo.cdef', 'w') as f: f.write(''.join(f'{line}\n' for line in cnt))
filenames = os.listdir(sys.argv[1]) # order header files by the same order that they are found in inside legato.h # so that e.g. typedefs are not referenced before declaration with open(os.path.join(os.environ["LEGATO_ROOT"], 'framework/include/', "legato.h")) as f: legatoh = f.readlines() def index_in_legatoh(header): return next((lineno for lineno, line in enumerate(legatoh) if line.startswith('#include "%s"' % header)), 100) filenames = sorted(filenames, key=lambda x: index_in_legatoh(x.replace('_cdef',''))) for fname in filenames: with open(os.path.join(sys.argv[1], fname)) as f: cdef += f.read() + "\n" m = re.search("le_(.+)_cdef.h",fname) if m: c_source += '#include <le_%s.h>\n' % m.group(1) else: print("Warning, wrong filename format for '%s', so we're not including the implementation header.") ffibuilder.cdef(cdef) ffibuilder.set_source("_liblegato_py", c_source) if __name__ == "__main__": ffibuilder.emit_c_code(os.path.join(sys.argv[2], "_liblegato_py.c"))
ffibuilder.set_source("gps_converter", r""" #include <math.h> #include <wgs84.h> void gps_to_euclidean(size_t num_pts, long double *input, long double *output) { const long double to_rad = M_PI / 180.0l; size_t i; for(i = 0; i < num_pts; ++i) { wgs84_to_euclidean( input[i*3] * to_rad, input[i*3 + 1] * to_rad, input[i*3 + 2], &output[i*3] ); } } """, extra_objects=['build/libwgs84.a'], include_dirs=['external/libwgs84/src']) ffibuilder.cdef(""" void gps_to_euclidean(size_t num_pts, long double *input, long double *output); """) if __name__ == "__main__": ffibuilder.emit_c_code("build/gps_converter.c")
cdef_types_lines = read_file(cdef_types_fn) cdef_api_lines = read_file(cdef_api_fn) cdef_callback_lines = read_file(sample_callback_fn) cdef_segments = [] cdef_segments.append(cdef_types_lines) cdef_segments.append(cdef_api_lines) cdef_segments.append(cdef_callback_lines) print("len(cdef_segments): %d" % len(cdef_segments)) for ndx in range(len(cdef_segments)): print("ndx: %d" % ndx) ffibuilder.cdef(cdef_segments[ndx]) if nocompile: c_fn = "%s.c" % module_name ffibuilder.emit_c_code(c_fn) else: ffibuilder.compile(verbose=True) # hack if sysver == 3: os.rename("_ddccffi3.cpython-36m-x86_64-linux-gnu.so", "_ddccffi3.so") # else: # os.rename("_ddccffi.so", "_ddccffi2.so")
ffibuilder = FFI() path = os.path.dirname(os.path.abspath(__file__)) cnt = [] with open(os.path.join(path, '..', 'libclingcon', 'clingcon.h')) as f: for line in f: if not re.match(r' *(#|//|extern *"C" *{|}$|$)', line): cnt.append( re.sub(r'[A-Z_]+_VISIBILITY_DEFAULT ', '', line).strip()) cnt.append( 'extern "Python" bool pyclingcon_rewrite(clingo_ast_t *ast, void *data);') code = '\n'.join(cnt) ffibuilder.cdef(f'''\ typedef uint64_t clingo_symbol_t; typedef struct clingo_ast_statement clingo_ast_statement_t; typedef struct clingo_ast clingo_ast_t; typedef struct clingo_control clingo_control_t; typedef struct clingo_options clingo_options_t; typedef struct clingo_model clingo_model_t; typedef struct clingo_statistics clingo_statistics_t; {code} ''') ffibuilder.set_source("_clingcon", """\ #include "clingcon.h" """) ffibuilder.emit_c_code('_clingcon.c')
from cffi import FFI from pathlib import Path ffi = FFI() ffi.set_source( "_flux._hostlist", """ #include <flux/hostlist.h> // TODO: remove this when we can use cffi 1.10 #ifdef __GNUC__ #pragma GCC visibility push(default) #endif """, ) cdefs = """ void free (void *); """ with open("_hostlist_preproc.h") as h: cdefs = cdefs + h.read() ffi.cdef(cdefs) if __name__ == "__main__": ffi.emit_c_code("_hostlist.c") # Ensure target mtime is updated, emit_c_code() may not do it Path("_hostlist.c").touch()
cdef = "" # Regexp for the C prototype of functions. Can be 'extern "Python" xxx yyyy' functionProto = re.compile('^\w+ [\w\"]+( \w+)*\n*\(\n*( +.*\n)+\);\n*', re.MULTILINE) # Regexp for the service instance name serviceInstancePtr = re.compile("const char\*\* \w*ServiceInstanceNamePtr;") # FIXME: cffi produces an error for typedef with parameter of type le_result_t. resultParam = re.compile(" le_result_t result,") # Add the cdef of the included APIs (USETYPES in the .api file) # But clean the cdef before to avoid errors. for apiFile in glob.glob(os.path.dirname(cdef_path) + "/includedApi/*_cdef.h"): with open(apiFile) as f: # Remove the functions. Keep only the typedefs, the structures # and the constants data = re.sub(functionProto, "", f.read()) data = re.sub(serviceInstancePtr, "", data) # TO REMOVE data = re.sub(resultParam, "int result,", data) cdef += data # Add the main cdef file with open(cdef_path) as f: cdef += f.read() ffibuilder.cdef(cdef, override=True) if __name__ == "__main__": ffibuilder.emit_c_code(os.path.join(out_dir, api_name+'_native.c'))
beer_err beer_renderer_add_sprite_node(struct BeerSprite *sprite, struct BeerRenderNode **r_node); beer_err beer_renderer_remove_node(struct BeerRenderNode *node); enum BeerKeyCode { BEER_KEY_UNKNOWN, BEER_KEY_W, BEER_KEY_A, BEER_KEY_S, BEER_KEY_D, BEER_KEY_ESC, BEER_KEY_SPACE, BEER_KEY_MAX }; struct BeerKeyState { bool pressed; }; beer_err beer_key_get_state(enum BeerKeyCode key, struct BeerKeyState *r_state); """) if __name__ == '__main__': ffibuilder.emit_c_code('_beer.c')
import argparse from cffi import FFI ffibuilder = FFI() if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--out", type=str, required=True) parser.add_argument("--cdef", type=str, required=True) parser.add_argument("--ext-name", type=str, required=True) parser.add_argument("--source", type=str, required=True) args = parser.parse_args() with open(args.cdef) as fp: ffibuilder.cdef(fp.read()) with open(args.source) as fp: ffibuilder.set_source(args.ext_name, fp.read(), compiler_verbose=False) ffibuilder.emit_c_code(args.out)
ffibuilder = FFI() path = os.path.dirname(os.path.abspath(__file__)) cnt = [] with open(os.path.join(path, '..', 'libclingo-lpx', 'clingo-lpx.h')) as f: for line in f: if not re.match(r' *(#|//|extern *"C" *{|}$|$)', line): cnt.append( re.sub(r'[A-Z_]+_VISIBILITY_DEFAULT ', '', line).strip()) cnt.append( 'extern "Python" bool pyclingolpx_rewrite(clingo_ast_t *ast, void *data);') code = '\n'.join(cnt) ffibuilder.cdef(f'''\ typedef uint64_t clingo_symbol_t; typedef struct clingo_ast_statement clingo_ast_statement_t; typedef struct clingo_ast clingo_ast_t; typedef struct clingo_control clingo_control_t; typedef struct clingo_options clingo_options_t; typedef struct clingo_model clingo_model_t; typedef struct clingo_statistics clingo_statistics_t; {code} ''') ffibuilder.set_source("_clingolpx", """\ #include "clingo-lpx.h" """) ffibuilder.emit_c_code('_clingolpx.c')
def generate_c(action): clingo_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) ffi = FFI() cnt = [] with open(f'{clingo_dir}/libclingo/clingo.h') as f: for line in f: if not re.match(r' *(#|//|extern *"C" *{|}$|$)', line): cnt.append( line.replace('CLINGO_VISIBILITY_DEFAULT ', '').strip()) # callbacks cnt.append( 'extern "Python" bool pyclingo_solve_event_callback(clingo_solve_event_type_t type, void *event, void *data, bool *goon);' ) cnt.append( 'extern "Python" void pyclingo_logger_callback(clingo_warning_t code, char const *message, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_ground_callback(clingo_location_t const *location, char const *name, clingo_symbol_t const *arguments, size_t arguments_size, void *data, clingo_symbol_callback_t symbol_callback, void *symbol_callback_data);' ) # propagator callbacks cnt.append( 'extern "Python" bool pyclingo_propagator_init(clingo_propagate_init_t *init, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_propagator_propagate(clingo_propagate_control_t *control, clingo_literal_t const *changes, size_t size, void *data);' ) cnt.append( 'extern "Python" void pyclingo_propagator_undo(clingo_propagate_control_t const *control, clingo_literal_t const *changes, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_propagator_check(clingo_propagate_control_t *control, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_propagator_decide(clingo_id_t thread_id, clingo_assignment_t const *assignment, clingo_literal_t fallback, void *data, clingo_literal_t *decision);' ) # observer callbacks cnt.append( 'extern "Python" bool pyclingo_observer_init_program(bool incremental, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_begin_step(void *data);') cnt.append('extern "Python" bool pyclingo_observer_end_step(void *data);') cnt.append( 'extern "Python" bool pyclingo_observer_rule(bool choice, clingo_atom_t const *head, size_t head_size, clingo_literal_t const *body, size_t body_size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_weight_rule(bool choice, clingo_atom_t const *head, size_t head_size, clingo_weight_t lower_bound, clingo_weighted_literal_t const *body, size_t body_size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_minimize(clingo_weight_t priority, clingo_weighted_literal_t const* literals, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_project(clingo_atom_t const *atoms, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_output_atom(clingo_symbol_t symbol, clingo_atom_t atom, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_output_term(clingo_symbol_t symbol, clingo_literal_t const *condition, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_output_csp(clingo_symbol_t symbol, int value, clingo_literal_t const *condition, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_external(clingo_atom_t atom, clingo_external_type_t type, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_assume(clingo_literal_t const *literals, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_heuristic(clingo_atom_t atom, clingo_heuristic_type_t type, int bias, unsigned priority, clingo_literal_t const *condition, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_acyc_edge(int node_u, int node_v, clingo_literal_t const *condition, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_term_number(clingo_id_t term_id, int number, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_term_string(clingo_id_t term_id, char const *name, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_term_compound(clingo_id_t term_id, int name_id_or_type, clingo_id_t const *arguments, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_element(clingo_id_t element_id, clingo_id_t const *terms, size_t terms_size, clingo_literal_t const *condition, size_t condition_size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_atom(clingo_id_t atom_id_or_zero, clingo_id_t term_id, clingo_id_t const *elements, size_t size, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_observer_theory_atom_with_guard(clingo_id_t atom_id_or_zero, clingo_id_t term_id, clingo_id_t const *elements, size_t size, clingo_id_t operator_id, clingo_id_t right_hand_side_id, void *data);' ) # application callbacks cnt.append( 'extern "Python" char const *pyclingo_application_program_name(void *data);' ) cnt.append( 'extern "Python" char const *pyclingo_application_version(void *data);' ) cnt.append( 'extern "Python" unsigned pyclingo_application_message_limit(void *data);' ) cnt.append( 'extern "Python" bool pyclingo_application_main(clingo_control_t *control, char const *const * files, size_t size, void *data);' ) cnt.append( 'extern "Python" void pyclingo_application_logger(clingo_warning_t code, char const *message, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_application_print_model(clingo_model_t const *model, clingo_default_model_printer_t printer, void *printer_data, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_application_register_options(clingo_options_t *options, void *data);' ) cnt.append( 'extern "Python" bool pyclingo_application_validate_options(void *data);' ) # application options callbacks cnt.append( 'extern "Python" bool pyclingo_application_options_parse(char const *value, void *data);' ) # ast callbacks cnt.append( 'extern "Python" bool pyclingo_ast_callback(clingo_ast_t const *, void *);' ) if action == "embed": ffi.embedding_api('''\ bool pyclingo_execute_(void *loc, char const *code, void *data); bool pyclingo_call_(void *loc, char const *name, void *arguments, size_t size, void *symbol_callback, void *symbol_callback_data, void *data); bool pyclingo_callable_(char const * name, bool *ret, void *data); bool pyclingo_main_(void *ctl, void *data); ''') ffi.embedding_init_code(f"""\ from collections.abc import Iterable from traceback import format_exception import __main__ from clingo._internal import _ffi, _handle_error, _lib from clingo.control import Control from clingo.symbol import Symbol def _cb_error_top_level(exception, exc_value, traceback): msg = "".join(format_exception(exception, exc_value, traceback)) _lib.clingo_set_error(_lib.clingo_error_runtime, msg.encode()) return False @_ffi.def_extern(onerror=_cb_error_top_level) def pyclingo_execute_(loc, code, data): exec(_ffi.string(code).decode(), __main__.__dict__, __main__.__dict__) return True @_ffi.def_extern(onerror=_cb_error_top_level) def pyclingo_call_(loc, name, arguments, size, symbol_callback, symbol_callback_data, data): symbol_callback = _ffi.cast('clingo_symbol_callback_t', symbol_callback) arguments = _ffi.cast('clingo_symbol_t*', arguments) context = _ffi.from_handle(data).data if data != _ffi.NULL else None py_name = _ffi.string(name).decode() fun = getattr(__main__ if context is None else context, py_name) args = [] for i in range(size): args.append(Symbol(arguments[i])) ret = fun(*args) symbols = list(ret) if isinstance(ret, Iterable) else [ret] c_symbols = _ffi.new('clingo_symbol_t[]', len(symbols)) for i, sym in enumerate(symbols): c_symbols[i] = sym._rep _handle_error(symbol_callback(c_symbols, len(symbols), symbol_callback_data)) return True @_ffi.def_extern(onerror=_cb_error_top_level) def pyclingo_callable_(name, ret, data): py_name = _ffi.string(name).decode() ret[0] = py_name in __main__.__dict__ and callable(__main__.__dict__[py_name]) return True @_ffi.def_extern(onerror=_cb_error_top_level) def pyclingo_main_(ctl, data): __main__.main(Control(_ffi.cast('clingo_control_t*', ctl))) return True """) if action != "header": ffi.set_source('_clingo', '#include <clingo.h>') ffi.cdef('\n'.join(cnt)) ffi.emit_c_code('_clingo.c') else: with open('_clingo.cdef', 'w') as f: f.write(''.join(f'{line}\n' for line in cnt))
def prepare(space, cdef, module_name, source, w_includes=None, w_extra_source=None): try: import cffi from cffi import FFI # <== the system one, which from cffi import recompiler # needs to be at least cffi 1.0.4 from cffi import ffiplatform except ImportError: py.test.skip("system cffi module not found or older than 1.0.0") if cffi.__version_info__ < (1, 2, 0): py.test.skip("system cffi module needs to be at least 1.2.0") space.appexec([], """(): import _cffi_backend # force it to be initialized """) includes = [] if w_includes: includes += space.unpackiterable(w_includes) assert module_name.startswith('test_') module_name = '_CFFI_' + module_name rdir = udir.ensure('recompiler', dir=1) rdir.join('Python.h').write('#define PYPY_VERSION XX\n' '#define PyMODINIT_FUNC /*exported*/ void\n') path = module_name.replace('.', os.sep) if '.' in module_name: subrdir = rdir.join(module_name[:module_name.index('.')]) os.mkdir(str(subrdir)) else: subrdir = rdir c_file = str(rdir.join('%s.c' % path)) ffi = FFI() for include_ffi_object in includes: ffi.include(include_ffi_object._test_recompiler_source_ffi) ffi.cdef(cdef) ffi.set_source(module_name, source) ffi.emit_c_code(c_file) base_module_name = module_name.split('.')[-1] sources = [] if w_extra_source is not None: sources.append(space.str_w(w_extra_source)) ext = ffiplatform.get_extension( c_file, module_name, include_dirs=[str(rdir)], export_symbols=['_cffi_pypyinit_' + base_module_name], sources=sources) ffiplatform.compile(str(rdir), ext) for extension in ['so', 'pyd', 'dylib']: so_file = str(rdir.join('%s.%s' % (path, extension))) if os.path.exists(so_file): break else: raise Exception("could not find the compiled extension module?") args_w = [space.wrap(module_name), space.wrap(so_file)] w_res = space.appexec( args_w, """(modulename, filename): import imp mod = imp.load_dynamic(modulename, filename) assert mod.__name__ == modulename return (mod.ffi, mod.lib) """) ffiobject = space.getitem(w_res, space.wrap(0)) ffiobject._test_recompiler_source_ffi = ffi if not hasattr(space, '_cleanup_ffi'): space._cleanup_ffi = [] space._cleanup_ffi.append(ffiobject) return w_res