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); }
void CFCC_write_man_pages(CFCC *self) { CFCHierarchy *hierarchy = self->hierarchy; CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); size_t num_classes = 0; for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (!CFCClass_included(klass)) { ++num_classes; } } char **man_pages = (char**)CALLOCATE(num_classes, sizeof(char*)); // Generate man pages, but don't write. That way, if there's an error // while generating the pages, we leak memory but don't clutter up the file // system. for (size_t i = 0, j = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass)) { continue; } char *man_page = CFCCMan_create_man_page(klass); man_pages[j++] = man_page; } const char *dest = CFCHierarchy_get_dest(hierarchy); char *man3_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "man" CHY_DIR_SEP "man3", dest); if (!CFCUtil_is_dir(man3_path)) { CFCUtil_make_path(man3_path); if (!CFCUtil_is_dir(man3_path)) { CFCUtil_die("Can't make path %s", man3_path); } } // Write out any man pages that have changed. for (size_t i = 0, j = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass)) { continue; } char *raw_man_page = man_pages[j++]; if (!raw_man_page) { continue; } char *man_page = CFCUtil_sprintf("%s%s%s", self->man_header, raw_man_page, self->man_footer); const char *full_struct_sym = CFCClass_full_struct_sym(klass); char *filename = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.3", man3_path, full_struct_sym); CFCUtil_write_if_changed(filename, man_page, strlen(man_page)); FREEMEM(filename); FREEMEM(man_page); FREEMEM(raw_man_page); } FREEMEM(man3_path); FREEMEM(man_pages); FREEMEM(ordered); }
void CFCPerlTypeMap_write_xs_typemap(CFCHierarchy *hierarchy) { CFCClass **classes = CFCHierarchy_ordered_classes(hierarchy); char *start = CFCUtil_strdup(""); char *input = CFCUtil_strdup(""); char *output = CFCUtil_strdup(""); for (int i = 0; classes[i] != NULL; i++) { CFCClass *klass = classes[i]; if (CFCClass_included(klass)) { continue; } const char *full_struct_sym = CFCClass_full_struct_sym(klass); const char *vtable_var = CFCClass_full_vtable_var(klass); start = CFCUtil_cat(start, full_struct_sym, "*\t", vtable_var, "_\n", NULL); input = CFCUtil_cat(input, vtable_var, "_\n" " $var = (", full_struct_sym, "*)XSBind_sv_to_cfish_obj($arg, ", vtable_var, ", NULL);\n\n", NULL); output = CFCUtil_cat(output, vtable_var, "_\n" " $arg = (SV*)Cfish_Obj_To_Host((cfish_Obj*)$var);\n" " CFISH_DECREF($var);\n" "\n", NULL); } char *content = CFCUtil_strdup(""); content = CFCUtil_cat(content, typemap_start, start, "\n\n", typemap_input, input, "\n\n", typemap_output, output, "\n\n", NULL); CFCUtil_write_if_changed("typemap", content, strlen(content)); FREEMEM(content); FREEMEM(output); FREEMEM(input); FREEMEM(start); FREEMEM(classes); }
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 CFCPerl_write_bindings(CFCPerl *self, const char *boot_class, CFCParcel **parcels) { CFCUTIL_NULL_CHECK(boot_class); CFCUTIL_NULL_CHECK(parcels); CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); CFCPerlClass **registry = CFCPerlClass_registry(); char *privacy_syms = CFCUtil_strdup(""); char *includes = CFCUtil_strdup(""); char *generated_xs = CFCUtil_strdup(""); char *class_specs = CFCUtil_strdup(""); char *xsub_specs = CFCUtil_strdup(""); char *bootstrap_calls = CFCUtil_strdup(""); char *hand_rolled_xs = CFCUtil_strdup(""); for (size_t i = 0; parcels[i]; ++i) { CFCParcel *parcel = parcels[i]; // Set host_module_name for parcel. if (!CFCParcel_included(parcel) && CFCParcel_is_installed(parcel)) { CFCParcel_set_host_module_name(parcel, boot_class); } // Bake the parcel privacy defines into the XS, so it can be compiled // without any extra compiler flags. const char *privacy_sym = CFCParcel_get_privacy_sym(parcel); privacy_syms = CFCUtil_cat(privacy_syms, "#define ", privacy_sym, "\n", NULL); // Bootstrap calls. const char *prefix = CFCParcel_get_prefix(parcel); includes = CFCUtil_cat(includes, "#include \"", prefix, "perl.h\"\n", NULL); bootstrap_calls = CFCUtil_cat(bootstrap_calls, " ", prefix, "bootstrap_perl();\n", NULL); } for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; CFCParcel *parcel = CFCClass_get_parcel(klass); int found = false; for (size_t j = 0; parcels[j]; j++) { if (parcel == parcels[j]) { found = true; break; } } if (!found) { continue; } // Pound-includes for generated headers. const char *include_h = CFCClass_include_h(klass); includes = CFCUtil_cat(includes, "#include \"", include_h, "\"\n", NULL); if (CFCClass_inert(klass)) { continue; } int num_xsubs = 0; // Constructors. CFCPerlConstructor **constructors = CFCPerlClass_constructor_bindings(klass); for (size_t j = 0; constructors[j] != NULL; j++) { CFCPerlSub *xsub = (CFCPerlSub*)constructors[j]; // Add the XSUB function definition. char *xsub_def = CFCPerlConstructor_xsub_def(constructors[j], klass); generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n", NULL); FREEMEM(xsub_def); // Add XSUB initialization at boot. xsub_specs = S_add_xsub_spec(xsub_specs, xsub); num_xsubs += 1; CFCBase_decref((CFCBase*)constructors[j]); } FREEMEM(constructors); // Methods. CFCPerlMethod **methods = CFCPerlClass_method_bindings(klass); for (size_t j = 0; methods[j] != NULL; j++) { CFCPerlSub *xsub = (CFCPerlSub*)methods[j]; // Add the XSUB function definition. char *xsub_def = CFCPerlMethod_xsub_def(methods[j], klass); generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n", NULL); FREEMEM(xsub_def); // Add XSUB initialization at boot. xsub_specs = S_add_xsub_spec(xsub_specs, xsub); num_xsubs += 1; CFCBase_decref((CFCBase*)methods[j]); } FREEMEM(methods); // Append XSBind_ClassSpec entry. const char *class_name = CFCClass_get_name(klass); CFCClass *parent = CFCClass_get_parent(klass); char *parent_name; if (parent) { parent_name = CFCUtil_sprintf("\"%s\"", CFCClass_get_name(parent)); } else { parent_name = CFCUtil_strdup("NULL"); } char *class_spec = CFCUtil_sprintf("{ \"%s\", %s, %d }", class_name, parent_name, num_xsubs); const char *sep = class_specs[0] == '\0' ? "" : ",\n"; class_specs = CFCUtil_cat(class_specs, sep, " ", class_spec, NULL); FREEMEM(class_spec); FREEMEM(parent_name); } // Hand-rolled XS. for (size_t i = 0; registry[i] != NULL; i++) { CFCPerlClass *perl_class = registry[i]; CFCParcel *parcel = CFCPerlClass_get_parcel(perl_class); int found = false; for (size_t j = 0; parcels[j]; j++) { if (parcel == parcels[j]) { found = true; break; } } if (!found) { continue; } const char *xs = CFCPerlClass_get_xs_code(perl_class); hand_rolled_xs = CFCUtil_cat(hand_rolled_xs, xs, "\n", NULL); } const char pattern[] = "%s" // Header. "\n" "%s" // Privacy syms. "\n" "#include \"XSBind.h\"\n" "%s" // Includes. "\n" "#ifndef XS_INTERNAL\n" " #define XS_INTERNAL XS\n" "#endif\n" "\n" "%s" // Generated XS. "\n" "MODULE = %s PACKAGE = %s\n" // Boot class. "\n" "BOOT:\n" "{\n" " static const cfish_XSBind_ClassSpec class_specs[] = {\n" "%s\n" // Class specs. " };\n" " static const cfish_XSBind_XSubSpec xsub_specs[] = {\n" "%s\n" // XSUB specs. " };\n" " size_t num_classes\n" " = sizeof(class_specs) / sizeof(class_specs[0]);\n" " const char* file = __FILE__;\n" "\n" "%s" // Bootstrap calls. "\n" " cfish_XSBind_bootstrap(aTHX_ num_classes, class_specs,\n" " xsub_specs, file);\n" "}\n" "\n" "%s" // Hand-rolled XS. "\n" "%s"; // Footer char *contents = CFCUtil_sprintf(pattern, self->c_header, privacy_syms, includes, generated_xs, boot_class, boot_class, class_specs, xsub_specs, bootstrap_calls, hand_rolled_xs, self->c_footer); // Derive path to generated .xs file. char *xs_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s.xs", self->lib_dir, boot_class); S_replace_double_colons(xs_path, CHY_DIR_SEP_CHAR); // Write out if there have been any changes. CFCUtil_write_if_changed(xs_path, contents, strlen(contents)); FREEMEM(xs_path); FREEMEM(contents); FREEMEM(hand_rolled_xs); FREEMEM(bootstrap_calls); FREEMEM(xsub_specs); FREEMEM(class_specs); FREEMEM(generated_xs); FREEMEM(includes); FREEMEM(privacy_syms); FREEMEM(ordered); }
static void S_write_callbacks_c(CFCPerl *self) { CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); static const char pattern[] = "%s" "\n" "#include \"XSBind.h\"\n" "#include \"callbacks.h\"\n" "\n" "static void\n" "S_finish_callback_void(const char *meth_name) {\n" " int count = call_method(meth_name, G_VOID | G_DISCARD);\n" " if (count != 0) {\n" " CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n" " meth_name, (int32_t)count);\n" " }\n" " FREETMPS;\n" " LEAVE;\n" "}\n" "\n" "static CFISH_INLINE SV*\n" "SI_do_callback_sv(const char *meth_name) {\n" " int count = call_method(meth_name, G_SCALAR);\n" " if (count != 1) {\n" " CFISH_THROW(CFISH_ERR, \"Bad callback to '%%s': %%i32\",\n" " meth_name, (int32_t)count);\n" " }\n" " dSP;\n" " SV *return_sv = POPs;\n" " PUTBACK;\n" " return return_sv;\n" "}\n" "\n" "static int64_t\n" "S_finish_callback_i64(const char *meth_name) {\n" " SV *return_sv = SI_do_callback_sv(meth_name);\n" " int64_t retval;\n" " if (sizeof(IV) == 8) {\n" " retval = (int64_t)SvIV(return_sv);\n" " }\n" " else {\n" " if (SvIOK(return_sv)) {\n" " // It's already no more than 32 bits, so don't convert.\n" " retval = SvIV(return_sv);\n" " }\n" " else {\n" " // Maybe lossy.\n" " double temp = SvNV(return_sv);\n" " retval = (int64_t)temp;\n" " }\n" " }\n" " FREETMPS;\n" " LEAVE;\n" " return retval;\n" "}\n" "\n" "static double\n" "S_finish_callback_f64(const char *meth_name) {\n" " SV *return_sv = SI_do_callback_sv(meth_name);\n" " double retval = SvNV(return_sv);\n" " FREETMPS;\n" " LEAVE;\n" " return retval;\n" "}\n" "\n" "static cfish_Obj*\n" "S_finish_callback_obj(void *vself, const char *meth_name,\n" " int nullable) {\n" " SV *return_sv = SI_do_callback_sv(meth_name);\n" " cfish_Obj *retval = XSBind_perl_to_cfish(return_sv);\n" " FREETMPS;\n" " LEAVE;\n" " if (!nullable && !retval) {\n" " CFISH_THROW(CFISH_ERR, \"%%o#%%s cannot return NULL\",\n" " CFISH_Obj_Get_Class_Name((cfish_Obj*)vself),\n" " meth_name);\n" " }\n" " return retval;\n" "}\n" "\n"; char *content = CFCUtil_sprintf(pattern, self->header); for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_inert(klass)) { 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 = CFCPerlMethod_callback_def(method); content = CFCUtil_cat(content, cb_def, "\n", NULL); FREEMEM(cb_def); } } FREEMEM(fresh_methods); } content = CFCUtil_cat(content, self->footer, NULL); // Write if changed. const char *src_dest = CFCHierarchy_get_source_dest(self->hierarchy); char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "callbacks.c", src_dest); CFCUtil_write_if_changed(filepath, content, strlen(content)); FREEMEM(filepath); FREEMEM(content); FREEMEM(ordered); }
void CFCPerl_write_bindings(CFCPerl *self) { CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); CFCPerlClass **registry = CFCPerlClass_registry(); char *hand_rolled_xs = CFCUtil_strdup(""); char *generated_xs = CFCUtil_strdup(""); char *xs_init = CFCUtil_strdup(""); // Pound-includes for generated headers. for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; // TODO: Don't include headers for parcels the source parcels don't // depend on. const char *include_h = CFCClass_include_h(klass); generated_xs = CFCUtil_cat(generated_xs, "#include \"", include_h, "\"\n", NULL); } generated_xs = CFCUtil_cat(generated_xs, "\n", NULL); for (size_t i = 0; ordered[i] != NULL; i++) { CFCClass *klass = ordered[i]; if (CFCClass_included(klass)) { continue; } // Constructors. CFCPerlConstructor **constructors = CFCPerlClass_constructor_bindings(klass); for (size_t j = 0; constructors[j] != NULL; j++) { CFCPerlSub *xsub = (CFCPerlSub*)constructors[j]; // Add the XSUB function definition. char *xsub_def = CFCPerlConstructor_xsub_def(constructors[j]); generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n", NULL); FREEMEM(xsub_def); // Add XSUB initialization at boot. xs_init = S_add_xs_init(xs_init, xsub); } FREEMEM(constructors); // Methods. CFCPerlMethod **methods = CFCPerlClass_method_bindings(klass); for (size_t j = 0; methods[j] != NULL; j++) { CFCPerlSub *xsub = (CFCPerlSub*)methods[j]; // Add the XSUB function definition. char *xsub_def = CFCPerlMethod_xsub_def(methods[j]); generated_xs = CFCUtil_cat(generated_xs, xsub_def, "\n", NULL); FREEMEM(xsub_def); // Add XSUB initialization at boot. xs_init = S_add_xs_init(xs_init, xsub); } FREEMEM(methods); } // Hand-rolled XS. for (size_t i = 0; registry[i] != NULL; i++) { const char *xs = CFCPerlClass_get_xs_code(registry[i]); hand_rolled_xs = CFCUtil_cat(hand_rolled_xs, xs, "\n", NULL); } // Write out if there have been any changes. char *xs_file_contents = S_xs_file_contents(self, generated_xs, xs_init, hand_rolled_xs); CFCUtil_write_if_changed(self->xs_path, xs_file_contents, strlen(xs_file_contents)); FREEMEM(xs_file_contents); FREEMEM(hand_rolled_xs); FREEMEM(xs_init); FREEMEM(generated_xs); FREEMEM(ordered); }