| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "charmony.h" |
| |
| #include <string.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <ctype.h> |
| |
| #define CFC_NEED_BASE_STRUCT_DEF |
| #include "CFCBase.h" |
| #include "CFCPython.h" |
| #include "CFCPyClass.h" |
| #include "CFCPyMethod.h" |
| #include "CFCParcel.h" |
| #include "CFCClass.h" |
| #include "CFCMethod.h" |
| #include "CFCHierarchy.h" |
| #include "CFCUtil.h" |
| #include "CFCBindCore.h" |
| |
| struct CFCPython { |
| CFCBase base; |
| CFCHierarchy *hierarchy; |
| char *header; |
| char *footer; |
| }; |
| |
| static void |
| S_destroy(CFCPython *self); |
| |
| static const CFCMeta CFCPYTHON_META = { |
| "Clownfish::CFC::Binding::Python", |
| sizeof(CFCPython), |
| (CFCBase_destroy_t)S_destroy |
| }; |
| |
| CFCPython* |
| CFCPython_new(CFCHierarchy *hierarchy) { |
| CFCUTIL_NULL_CHECK(hierarchy); |
| CFCPython *self = (CFCPython*)CFCBase_allocate(&CFCPYTHON_META); |
| self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); |
| self->header = CFCUtil_strdup(""); |
| self->footer = CFCUtil_strdup(""); |
| return self; |
| } |
| |
| static void |
| S_destroy(CFCPython *self) { |
| CFCBase_decref((CFCBase*)self->hierarchy); |
| FREEMEM(self->header); |
| FREEMEM(self->footer); |
| CFCBase_destroy((CFCBase*)self); |
| } |
| |
| void |
| CFCPython_set_header(CFCPython *self, const char *header) { |
| CFCUTIL_NULL_CHECK(header); |
| free(self->header); |
| self->header = CFCUtil_make_c_comment(header); |
| } |
| |
| void |
| CFCPython_set_footer(CFCPython *self, const char *footer) { |
| CFCUTIL_NULL_CHECK(footer); |
| free(self->footer); |
| self->footer = CFCUtil_make_c_comment(footer); |
| } |
| |
| static void |
| S_write_hostdefs(CFCPython *self) { |
| const char pattern[] = |
| "%s\n" |
| "\n" |
| "#ifndef H_CFISH_HOSTDEFS\n" |
| "#define H_CFISH_HOSTDEFS 1\n" |
| "\n" |
| "#include \"Python.h\"\n" |
| "\n" |
| "#define CFISH_OBJ_HEAD \\\n" |
| " PyObject_HEAD\n" |
| "\n" |
| "#endif /* H_CFISH_HOSTDEFS */\n" |
| "\n" |
| "%s\n"; |
| char *content |
| = CFCUtil_sprintf(pattern, self->header, self->footer); |
| |
| // Write if the content has changed. |
| const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy); |
| char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_hostdefs.h", |
| inc_dest); |
| CFCUtil_write_if_changed(filepath, content, strlen(content)); |
| |
| FREEMEM(filepath); |
| FREEMEM(content); |
| } |
| |
| static char* |
| S_gen_callbacks(CFCPython *self, CFCParcel *parcel, CFCClass **ordered) { |
| char *callbacks = CFCUtil_strdup(""); |
| |
| // Generate implementation files containing callback definitions. |
| for (size_t i = 0; ordered[i] != NULL; i++) { |
| CFCClass *klass = ordered[i]; |
| if (CFCClass_included(klass) |
| || CFCClass_inert(klass) |
| //|| CFCClass_get_parcel(klass) != parcel |
| ) { |
| continue; |
| } |
| |
| CFCMethod **fresh_methods = CFCClass_fresh_methods(klass); |
| for (int meth_num = 0; fresh_methods[meth_num] != NULL; meth_num++) { |
| CFCMethod *method = fresh_methods[meth_num]; |
| |
| // Define callback. |
| if (CFCMethod_novel(method) && !CFCMethod_final(method)) { |
| char *cb_def = CFCPyMethod_callback_def(method, klass); |
| callbacks = CFCUtil_cat(callbacks, cb_def, "\n", NULL); |
| FREEMEM(cb_def); |
| } |
| } |
| } |
| |
| static const char helpers[] = |
| "static PyObject*\n" |
| "S_pack_tuple(int num_args, ...) {\n" |
| " PyObject *tuple = PyTuple_New(num_args);\n" |
| " va_list args;\n" |
| " va_start(args, num_args);\n" |
| " for (int i = 0; i < num_args; i++) {\n" |
| " PyObject *arg = va_arg(args, PyObject*);\n" |
| " PyTuple_SET_ITEM(tuple, i, arg);\n" |
| " }\n" |
| " va_end(args);\n" |
| " return tuple;\n" |
| "}\n" |
| "#define CFBIND_TRY(routine) \\\n" |
| " do { \\\n" |
| " jmp_buf env; \\\n" |
| " jmp_buf *prev_env = CFBind_swap_env(&env); \\\n" |
| " if (!setjmp(env)) { \\\n" |
| " routine; \\\n" |
| " } \\\n" |
| " CFBind_swap_env(prev_env); \\\n" |
| " } while (0)\n" |
| "\n" |
| "static PyObject*\n" |
| "S_call_pymeth(PyObject *self, const char *meth_name, PyObject *args,\n" |
| " const char *file, int line, const char *func) {\n" |
| " PyObject *callable = PyObject_GetAttrString(self, meth_name);\n" |
| " if (!PyCallable_Check(callable)) {\n" |
| " cfish_String *mess\n" |
| " = cfish_Err_make_mess(file, line, func, \"Attr '%s' not callable\",\n" |
| " meth_name);\n" |
| " cfish_Err_throw_mess(CFISH_ERR, mess);\n" |
| " }\n" |
| " PyObject *result = PyObject_CallObject(callable, args);\n" |
| " Py_DECREF(args);\n" |
| " if (result == NULL) {\n" |
| " cfish_String *mess\n" |
| " = cfish_Err_make_mess(file, line, func,\n" |
| " \"Callback to '%s' failed\", meth_name);\n" |
| " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" |
| " }\n" |
| " return result;\n" |
| "}\n" |
| "\n" |
| "#define CALL_PYMETH_VOID(self, meth_name, args) \\\n" |
| " S_call_pymeth_void(self, meth_name, args, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" |
| "\n" |
| "static void\n" |
| "S_call_pymeth_void(PyObject *self, const char *meth_name, PyObject *args,\n" |
| " const char *file, int line, const char *func) {\n" |
| " PyObject *py_result\n" |
| " = S_call_pymeth(self, meth_name, args, file, line, func);\n" |
| " if (py_result == NULL) {\n" |
| " cfish_String *mess\n" |
| " = cfish_Err_make_mess(file, line, func, \"Call to %s failed\",\n" |
| " meth_name);\n" |
| " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" |
| " }\n" |
| " Py_DECREF(py_result);\n" |
| "}\n" |
| "\n" |
| "#define CALL_PYMETH_BOOL(self, meth_name, args) \\\n" |
| " S_call_pymeth_bool(self, meth_name, args, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" |
| "\n" |
| "static bool\n" |
| "S_call_pymeth_bool(PyObject *self, const char *meth_name, PyObject *args,\n" |
| " const char *file, int line, const char *func) {\n" |
| " PyObject *py_result\n" |
| " = S_call_pymeth(self, meth_name, args, file, line, func);\n" |
| " int truthiness = py_result != NULL\n" |
| " ? PyObject_IsTrue(py_result)\n" |
| " : -1;\n" |
| " if (truthiness == -1) {\n" |
| " cfish_String *mess\n" |
| " = cfish_Err_make_mess(file, line, func, \"Call to %s failed\",\n" |
| " meth_name);\n" |
| " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" |
| " }\n" |
| " Py_DECREF(py_result);\n" |
| " return !!truthiness;\n" |
| "}\n" |
| "\n" |
| "#define CALL_PYMETH_OBJ(self, meth_name, args, ret_class, nullable) \\\n" |
| " S_call_pymeth_obj(self, meth_name, args, ret_class, nullable, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" |
| "\n" |
| "static cfish_Obj*\n" |
| "S_call_pymeth_obj(PyObject *self, const char *meth_name,\n" |
| " PyObject *args, cfish_Class *ret_class, bool nullable,\n" |
| " const char *file, int line, const char *func) {\n" |
| " PyObject *py_result\n" |
| " = S_call_pymeth(self, meth_name, args, file, line, func);\n" |
| " cfish_Obj *result = CFBind_py_to_cfish(py_result, ret_class);\n" |
| " Py_DECREF(py_result);\n" |
| " if (!nullable && result == NULL) {\n" |
| " CFISH_THROW(CFISH_ERR, \"%s cannot return NULL\", meth_name);\n" |
| " }\n" |
| " else if (!cfish_Obj_is_a(result, ret_class)) {\n" |
| " cfish_Class *result_class = cfish_Obj_get_class(result);\n" |
| " CFISH_DECREF(result);\n" |
| " CFISH_THROW(CFISH_ERR, \"%s returned %o instead of %o\", meth_name,\n" |
| " CFISH_Class_Get_Name(result_class),\n" |
| " CFISH_Class_Get_Name(ret_class));\n" |
| " }\n" |
| " return result;\n" |
| "}\n" |
| "\n" |
| "#define CALL_PYMETH_DOUBLE(self, meth_name, args) \\\n" |
| " S_call_pymeth_f64(self, meth_name, args, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" |
| "#define CALL_PYMETH_FLOAT(self, meth_name, args) \\\n" |
| " ((float)S_call_pymeth_f64(self, meth_name, args, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "\n" |
| "static double\n" |
| "S_call_pymeth_f64(PyObject *self, const char *meth_name, PyObject *args,\n" |
| " const char *file, int line, const char *func) {\n" |
| " PyObject *py_result\n" |
| " = S_call_pymeth(self, meth_name, args, file, line, func);\n" |
| " PyErr_Clear();\n" |
| " double result = PyFloat_AsDouble(py_result);\n" |
| " if (PyErr_Occurred()) {\n" |
| " cfish_String *mess\n" |
| " = cfish_Err_make_mess(file, line, func,\n" |
| " \"Converting result of '%s' to double failed\",\n" |
| " meth_name);\n" |
| " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" |
| " }\n" |
| " Py_DECREF(py_result);\n" |
| " return result;\n" |
| "}\n" |
| "\n" |
| "#define CALL_PYMETH_INT64_T(self, meth_name, args) \\\n" |
| " S_call_pymeth_i64(self, meth_name, args, INT64_MAX, INT64_MIN, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" |
| "#define CALL_PYMETH_INT32_T(self, meth_name, args) \\\n" |
| " ((int32_t)S_call_pymeth_i64(self, meth_name, args, INT32_MAX, INT32_MIN, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_INT16_T(self, meth_name, args) \\\n" |
| " ((int16_t)S_call_pymeth_i64(self, meth_name, args, INT16_MAX, INT16_MIN, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_INT8_T(self, meth_name, args) \\\n" |
| " ((int8_t)S_call_pymeth_i64(self, meth_name, args, INT8_MAX, INT8_MIN, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_CHAR(self, meth_name, args) \\\n" |
| " ((char)S_call_pymeth_i64(self, meth_name, args, CHAR_MAX, CHAR_MIN, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_SHORT(self, meth_name, args) \\\n" |
| " ((short)S_call_pymeth_i64(self, meth_name, args, SHRT_MAX, SHRT_MIN, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_INT(self, meth_name, args) \\\n" |
| " ((int16_t)S_call_pymeth_i64(self, meth_name, args, INT_MAX, INT_MIN, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_LONG(self, meth_name, args) \\\n" |
| " ((int16_t)S_call_pymeth_i64(self, meth_name, args, LONG_MAX, LONG_MIN, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "\n" |
| "static int64_t\n" |
| "S_call_pymeth_i64(PyObject *self, const char *meth_name, PyObject *args,\n" |
| " int64_t max, int64_t min,\n" |
| " const char *file, int line, const char *func) {\n" |
| " PyObject *py_result\n" |
| " = S_call_pymeth(self, meth_name, args, file, line, func);\n" |
| " PyErr_Clear();\n" |
| " int64_t result = PyLong_AsLongLong(py_result);\n" |
| " if (PyErr_Occurred() || result > max || result < min) {\n" |
| " cfish_String *mess\n" |
| " = cfish_Err_make_mess(file, line, func,\n" |
| " \"Converting result of '%s' to int64_t failed\",\n" |
| " meth_name);\n" |
| " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" |
| " }\n" |
| " Py_DECREF(py_result);\n" |
| " return result;\n" |
| "}\n" |
| "\n" |
| "#define CALL_PYMETH_UINT64_T(self, meth_name, args) \\\n" |
| " S_call_pymeth_u64(self, meth_name, args, UINT64_MAX, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" |
| "#define CALL_PYMETH_UINT32_T(self, meth_name, args) \\\n" |
| " ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT32_MAX, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_UINT16_T(self, meth_name, args) \\\n" |
| " ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT16_MAX, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_UINT8_T(self, meth_name, args) \\\n" |
| " ((uint32_t)S_call_pymeth_u64(self, meth_name, args, UINT8_MAX, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO))\n" |
| "#define CALL_PYMETH_SIZE_T(self, meth_name, args) \\\n" |
| " S_call_pymeth_u64(self, meth_name, args, SIZE_MAX, \\\n" |
| " __FILE__, __LINE__, CFISH_ERR_FUNC_MACRO)\n" |
| "\n" |
| "static uint64_t\n" |
| "S_call_pymeth_u64(PyObject *self, const char *meth_name, PyObject *args,\n" |
| " uint64_t max,\n" |
| " const char *file, int line, const char *func) {\n" |
| " PyObject *py_result\n" |
| " = S_call_pymeth(self, meth_name, args, file, line, func);\n" |
| " PyErr_Clear();\n" |
| " uint64_t result = PyLong_AsUnsignedLongLong(py_result);\n" |
| " if (PyErr_Occurred()) {\n" |
| " cfish_String *mess\n" |
| " = cfish_Err_make_mess(file, line, func,\n" |
| " \"Converting result of '%s' to uint64_t failed\",\n" |
| " meth_name);\n" |
| " CFBind_reraise_pyerr(CFISH_ERR, mess);\n" |
| " }\n" |
| " Py_DECREF(py_result);\n" |
| " return result;\n" |
| "}\n" |
| ; |
| |
| static const char pattern[] = |
| "%s\n" |
| "\n" |
| "%s" |
| ; |
| char *content = CFCUtil_sprintf(pattern, helpers, callbacks); |
| |
| FREEMEM(callbacks); |
| return content; |
| } |
| |
| static char* |
| S_gen_type_linkups(CFCPython *self, CFCParcel *parcel, CFCClass **ordered) { |
| char *handles = CFCUtil_strdup(""); |
| char *py_types = CFCUtil_strdup(""); |
| int num_items = 0; |
| |
| for (size_t i = 0; ordered[i] != NULL; i++) { |
| CFCClass *klass = ordered[i]; |
| if (CFCClass_included(klass) || CFCClass_inert(klass)) { |
| continue; |
| } |
| const char *class_var = CFCClass_full_class_var(klass); |
| const char *struct_sym = CFCClass_get_struct_sym(klass); |
| char *handles_temp |
| = CFCUtil_sprintf("%s handles[%d] = &%s;\n", |
| handles, num_items, class_var); |
| char *py_types_temp |
| = CFCUtil_sprintf("%s py_types[%d] = &%s_pytype_struct;\n", |
| py_types, num_items, struct_sym); |
| FREEMEM(handles); |
| FREEMEM(py_types); |
| handles = handles_temp; |
| py_types = py_types_temp; |
| num_items++; |
| } |
| |
| char pattern[] = |
| "static void\n" |
| "S_link_py_types(void) {\n" |
| " const int num_items = %d;\n" |
| " size_t handles_size = num_items * sizeof(cfish_Class**);\n" |
| " size_t py_types_size = num_items * sizeof(PyTypeObject*);\n" |
| " cfish_Class ***handles = (cfish_Class***)CFISH_MALLOCATE(handles_size);\n" |
| " PyTypeObject **py_types = (PyTypeObject**)CFISH_MALLOCATE(py_types_size);\n" |
| "%s\n" |
| "%s\n" |
| " CFBind_assoc_py_types(handles, py_types, num_items);\n" |
| " CFISH_FREEMEM(handles);\n" |
| " CFISH_FREEMEM(py_types);\n" |
| "}\n" |
| ; |
| char *content = CFCUtil_sprintf(pattern, num_items, handles, py_types); |
| |
| FREEMEM(handles); |
| FREEMEM(py_types); |
| return content; |
| } |
| |
| static char* |
| S_gen_class_bindings(CFCPython *self, CFCParcel *parcel, |
| const char *pymod_name, CFCClass **ordered) { |
| char *bindings = CFCUtil_strdup(""); |
| for (size_t i = 0; ordered[i] != NULL; i++) { |
| CFCClass *klass = ordered[i]; |
| if (CFCClass_included(klass)) { |
| continue; |
| } |
| const char *class_name = CFCClass_get_name(klass); |
| CFCPyClass *class_binding = CFCPyClass_singleton(class_name); |
| if (!class_binding) { |
| // No binding spec'd out, so create one using defaults. |
| class_binding = CFCPyClass_new(klass); |
| CFCPyClass_add_to_registry(class_binding); |
| } |
| |
| char *code = CFCPyClass_gen_binding_code(class_binding); |
| bindings = CFCUtil_cat(bindings, code, NULL); |
| FREEMEM(code); |
| } |
| return bindings; |
| } |
| |
| static void |
| S_write_module_file(CFCPython *self, CFCParcel *parcel, const char *dest) { |
| const char *parcel_name = CFCParcel_get_name(parcel); |
| char *pymod_name = CFCUtil_strdup(parcel_name); |
| // TODO: Stop lowercasing when parcels are restricted to lowercase. |
| for (int i = 0; pymod_name[i] != '\0'; i++) { |
| pymod_name[i] = tolower(pymod_name[i]); |
| } |
| const char *last_dot = strrchr(pymod_name, '.'); |
| const char *last_component = last_dot != NULL |
| ? last_dot + 1 |
| : pymod_name; |
| char *helper_mod_name = CFCUtil_sprintf("%s._%s", pymod_name, last_component); |
| for (int i = 0; helper_mod_name[i] != '\0'; i++) { |
| helper_mod_name[i] = tolower(helper_mod_name[i]); |
| } |
| |
| CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); |
| CFCParcel **parcels = CFCParcel_all_parcels(); |
| char *callbacks = S_gen_callbacks(self, parcel, ordered); |
| char *type_linkups = S_gen_type_linkups(self, parcel, ordered); |
| char *pound_includes = CFCUtil_strdup(""); |
| char *class_bindings = S_gen_class_bindings(self, parcel, pymod_name, ordered); |
| char *parcel_boots = CFCUtil_strdup(""); |
| char *pytype_ready_calls = CFCUtil_strdup(""); |
| char *module_adds = CFCUtil_strdup(""); |
| |
| // Add parcel bootstrapping calls. |
| for (size_t i = 0; parcels[i]; ++i) { |
| if (!CFCParcel_included(parcels[i])) { |
| const char *prefix = CFCParcel_get_prefix(parcels[i]); |
| parcel_boots = CFCUtil_cat(parcel_boots, " ", prefix, |
| "bootstrap_parcel();\n", NULL); |
| } |
| } |
| |
| for (size_t i = 0; ordered[i] != NULL; i++) { |
| CFCClass *klass = ordered[i]; |
| if (CFCClass_included(klass)) { continue; } |
| const char *struct_sym = CFCClass_get_struct_sym(klass); |
| |
| const char *include_h = CFCClass_include_h(klass); |
| pound_includes = CFCUtil_cat(pound_includes, "#include \"", |
| include_h, "\"\n", NULL); |
| |
| // The PyType_Ready invocations for instantiable classes are handled |
| // via bootstrapping of Clownfish Class objects. Since inert classes |
| // do not at present have Class objects, we need to handle their |
| // PyType_Ready calls independently. |
| if (CFCClass_inert(klass)) { |
| pytype_ready_calls = CFCUtil_cat(pytype_ready_calls, |
| " if (PyType_Ready(&", struct_sym, |
| "_pytype_struct) < 0) { return NULL; }\n", NULL); |
| } |
| |
| module_adds = CFCUtil_cat(module_adds, " PyModule_AddObject(module, \"", |
| struct_sym, "\", (PyObject*)&", struct_sym, |
| "_pytype_struct);\n", NULL); |
| } |
| |
| const char pattern[] = |
| "%s\n" |
| "\n" |
| "#include \"Python.h\"\n" |
| "#include \"cfish_parcel.h\"\n" |
| "#include \"CFBind.h\"\n" |
| "%s\n" |
| "\n" |
| "%s\n" // callbacks |
| "\n" |
| "static PyModuleDef module_def = {\n" |
| " PyModuleDef_HEAD_INIT,\n" |
| " \"%s\",\n" // module name |
| " NULL,\n" // docstring |
| " -1,\n" |
| " NULL, NULL, NULL, NULL, NULL\n" |
| "};\n" |
| "\n" |
| "%s" // class bindings |
| "\n" |
| "%s" // S_link_py_types function |
| "\n" |
| "PyMODINIT_FUNC\n" |
| "PyInit__%s(void) {\n" |
| " cfish_Class_bootstrap_hook1 = CFBind_class_bootstrap_hook1;\n" |
| "\n" |
| "%s\n" // PyType_Ready calls |
| "\n" |
| " S_link_py_types();\n" |
| "\n" |
| "%s\n" // parcel boots |
| "\n" |
| " PyObject *module = PyModule_Create(&module_def);\n" |
| "%s\n" // Add types to module |
| "\n" |
| " return module;\n" |
| "}\n" |
| "\n" |
| "%s\n" |
| "\n"; |
| |
| char *content |
| = CFCUtil_sprintf(pattern, self->header, pound_includes, callbacks, |
| helper_mod_name, class_bindings, type_linkups, |
| last_component, pytype_ready_calls, parcel_boots, |
| module_adds, self->footer); |
| |
| char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "_%s.c", dest, |
| last_component); |
| CFCUtil_write_if_changed(filepath, content, strlen(content)); |
| FREEMEM(filepath); |
| |
| FREEMEM(content); |
| FREEMEM(module_adds); |
| FREEMEM(pytype_ready_calls); |
| FREEMEM(parcel_boots); |
| FREEMEM(class_bindings); |
| FREEMEM(helper_mod_name); |
| FREEMEM(pymod_name); |
| FREEMEM(pound_includes); |
| FREEMEM(type_linkups); |
| FREEMEM(callbacks); |
| FREEMEM(ordered); |
| } |
| |
| void |
| CFCPython_write_bindings(CFCPython *self, const char *parcel_name, const char *dest) { |
| CFCParcel *parcel = CFCParcel_fetch(parcel_name); |
| if (parcel == NULL) { |
| CFCUtil_die("Unknown parcel: %s", parcel_name); |
| } |
| S_write_hostdefs(self); |
| S_write_module_file(self, parcel, dest); |
| } |
| |