| /* 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> |
| |
| #define CFC_NEED_BASE_STRUCT_DEF |
| #include "CFCBase.h" |
| #include "CFCBindCore.h" |
| #include "CFCBindClass.h" |
| #include "CFCBindFile.h" |
| #include "CFCBindSpecs.h" |
| #include "CFCClass.h" |
| #include "CFCFile.h" |
| #include "CFCHierarchy.h" |
| #include "CFCParcel.h" |
| #include "CFCUtil.h" |
| #include "CFCVersion.h" |
| |
| #define STRING(s) #s |
| #define XSTRING(s) STRING(s) |
| |
| struct CFCBindCore { |
| CFCBase base; |
| CFCHierarchy *hierarchy; |
| char *c_header; |
| char *c_footer; |
| }; |
| |
| /* Write the "parcel.h" header file, which contains common symbols needed by |
| * all classes, plus typedefs for all class structs. |
| */ |
| static void |
| S_write_parcel_h(CFCBindCore *self, CFCParcel *parcel); |
| |
| /* Write the "parcel.c" file containing autogenerated implementation code. |
| */ |
| static void |
| S_write_parcel_c(CFCBindCore *self, CFCParcel *parcel); |
| |
| /* Write the "cfish_platform.h" header file, which contains platform-specific |
| * definitions. |
| */ |
| static void |
| S_write_platform_h(CFCBindCore *self); |
| |
| static char* |
| S_charmony_feature_defines(); |
| |
| static char* |
| S_charmony_string_defines(); |
| |
| static char* |
| S_charmony_stdbool_defines(); |
| |
| static char* |
| S_charmony_stdint_defines(); |
| |
| static char* |
| S_charmony_alloca_defines(); |
| |
| static void |
| S_write_host_data_json(CFCBindCore *self, CFCParcel *parcel, |
| const char *dest_dir, const char *host_lang); |
| |
| static const CFCMeta CFCBINDCORE_META = { |
| "Clownfish::CFC::Binding::Core", |
| sizeof(CFCBindCore), |
| (CFCBase_destroy_t)CFCBindCore_destroy |
| }; |
| |
| CFCBindCore* |
| CFCBindCore_new(CFCHierarchy *hierarchy, const char *header, |
| const char *footer) { |
| CFCBindCore *self = (CFCBindCore*)CFCBase_allocate(&CFCBINDCORE_META); |
| return CFCBindCore_init(self, hierarchy, header, footer); |
| } |
| |
| CFCBindCore* |
| CFCBindCore_init(CFCBindCore *self, CFCHierarchy *hierarchy, |
| const char *header, const char *footer) { |
| CFCUTIL_NULL_CHECK(hierarchy); |
| CFCUTIL_NULL_CHECK(header); |
| CFCUTIL_NULL_CHECK(footer); |
| self->hierarchy = (CFCHierarchy*)CFCBase_incref((CFCBase*)hierarchy); |
| self->c_header = CFCUtil_make_c_comment(header); |
| self->c_footer = CFCUtil_make_c_comment(footer); |
| return self; |
| } |
| |
| void |
| CFCBindCore_destroy(CFCBindCore *self) { |
| CFCBase_decref((CFCBase*)self->hierarchy); |
| FREEMEM(self->c_header); |
| FREEMEM(self->c_footer); |
| CFCBase_destroy((CFCBase*)self); |
| } |
| |
| int |
| CFCBindCore_write_all_modified(CFCBindCore *self, int modified) { |
| CFCHierarchy *hierarchy = self->hierarchy; |
| const char *header = self->c_header; |
| const char *footer = self->c_footer; |
| |
| // Discover whether files need to be regenerated. |
| modified = CFCHierarchy_propagate_modified(hierarchy, modified); |
| |
| // Iterate over all File objects, writing out those which don't have |
| // up-to-date auto-generated files. |
| const char *inc_dest = CFCHierarchy_get_include_dest(hierarchy); |
| CFCFile **files = CFCHierarchy_files(hierarchy); |
| for (int i = 0; files[i] != NULL; i++) { |
| if (CFCFile_get_modified(files[i])) { |
| CFCBindFile_write_h(files[i], inc_dest, header, footer); |
| } |
| } |
| |
| // If any class definition has changed, rewrite the parcel.h and parcel.c |
| // files. |
| if (modified) { |
| S_write_platform_h(self); |
| |
| CFCParcel **parcels = CFCParcel_all_parcels(); |
| for (size_t i = 0; parcels[i]; ++i) { |
| CFCParcel *parcel = parcels[i]; |
| S_write_parcel_h(self, parcel); |
| if (!CFCParcel_included(parcel)) { |
| S_write_parcel_c(self, parcel); |
| } |
| } |
| } |
| |
| return modified; |
| } |
| |
| /* Write the "parcel.h" header file, which contains common symbols needed by |
| * all classes, plus typedefs for all class structs. |
| */ |
| static void |
| S_write_parcel_h(CFCBindCore *self, CFCParcel *parcel) { |
| CFCHierarchy *hierarchy = self->hierarchy; |
| const char *prefix = CFCParcel_get_prefix(parcel); |
| const char *PREFIX = CFCParcel_get_PREFIX(parcel); |
| const char *privacy_sym = CFCParcel_get_privacy_sym(parcel); |
| |
| // Declare object structs and class singletons for all instantiable |
| // classes. |
| char *typedefs = CFCUtil_strdup(""); |
| char *class_decls = CFCUtil_strdup(""); |
| CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); |
| for (int i = 0; ordered[i] != NULL; i++) { |
| CFCClass *klass = ordered[i]; |
| const char *class_prefix = CFCClass_get_prefix(klass); |
| if (strcmp(class_prefix, prefix) != 0) { continue; } |
| |
| if (!CFCClass_inert(klass)) { |
| const char *full_struct = CFCClass_full_struct_sym(klass); |
| typedefs = CFCUtil_cat(typedefs, "typedef struct ", full_struct, |
| " ", full_struct, ";\n", NULL); |
| const char *class_var = CFCClass_full_class_var(klass); |
| class_decls = CFCUtil_cat(class_decls, "extern ", PREFIX, |
| "VISIBLE cfish_Class *", class_var, |
| ";\n", NULL); |
| } |
| } |
| FREEMEM(ordered); |
| |
| // Special includes and macros for Clownfish parcel. |
| const char *cfish_includes = |
| "#include <stdarg.h>\n" |
| "#include <stddef.h>\n" |
| "\n" |
| "#include \"cfish_platform.h\"\n" |
| "#include \"cfish_hostdefs.h\"\n"; |
| |
| // Special definitions for Clownfish parcel. |
| const char *cfish_defs_1 = |
| "#define CFISH_UNUSED_VAR(var) ((void)var)\n" |
| "#define CFISH_UNREACHABLE_RETURN(type) return (type)0\n" |
| "\n" |
| "/* Generic method pointer.\n" |
| " */\n" |
| "typedef void\n" |
| "(*cfish_method_t)(const void *vself);\n" |
| "\n" |
| "/* Access the function pointer for a given method from the class.\n" |
| " */\n" |
| "#define CFISH_METHOD_PTR(_class, _full_meth) \\\n" |
| " ((_full_meth ## _t)cfish_method(_class, _full_meth ## _OFFSET))\n" |
| "\n" |
| "static CFISH_INLINE cfish_method_t\n" |
| "cfish_method(const void *klass, uint32_t offset) {\n" |
| " union { char *cptr; cfish_method_t *fptr; } ptr;\n" |
| " ptr.cptr = (char*)klass + offset;\n" |
| " return ptr.fptr[0];\n" |
| "}\n" |
| "\n" |
| "typedef struct cfish_Dummy {\n" |
| " CFISH_OBJ_HEAD\n" |
| " void *klass;\n" |
| "} cfish_Dummy;\n" |
| "\n" |
| "/* Access the function pointer for a given method from the object.\n" |
| " */\n" |
| "static CFISH_INLINE cfish_method_t\n" |
| "cfish_obj_method(const void *object, uint32_t offset) {\n" |
| " cfish_Dummy *dummy = (cfish_Dummy*)object;\n" |
| " return cfish_method(dummy->klass, offset);\n" |
| "}\n" |
| "\n" |
| "/* Access the function pointer for the given method in the\n" |
| " * superclass. */\n" |
| "#define CFISH_SUPER_METHOD_PTR(_class, _full_meth) \\\n" |
| " ((_full_meth ## _t)cfish_super_method(_class, \\\n" |
| " _full_meth ## _OFFSET))\n" |
| "\n" |
| "extern CFISH_VISIBLE uint32_t cfish_Class_offset_of_parent;\n" |
| "static CFISH_INLINE cfish_method_t\n" |
| "cfish_super_method(const void *klass, uint32_t offset) {\n" |
| " char *class_as_char = (char*)klass;\n" |
| " cfish_Class **parent_ptr\n" |
| " = (cfish_Class**)(class_as_char + cfish_Class_offset_of_parent);\n" |
| " return cfish_method(*parent_ptr, offset);\n" |
| "}\n" |
| "\n" |
| "typedef void\n" |
| "(*cfish_destroy_t)(void *vself);\n" |
| "extern CFISH_VISIBLE uint32_t CFISH_Obj_Destroy_OFFSET;\n" |
| "\n" |
| "/** Invoke the [](.Destroy) method found in `klass` on\n" |
| " * `self`.\n" |
| " *\n" |
| " * TODO: Eliminate this function if we can arrive at a proper SUPER syntax.\n" |
| " */\n" |
| "static CFISH_INLINE void\n" |
| "cfish_super_destroy(void *vself, cfish_Class *klass) {\n" |
| " cfish_Obj *self = (cfish_Obj*)vself;\n" |
| " if (self != NULL) {\n" |
| " cfish_destroy_t super_destroy\n" |
| " = (cfish_destroy_t)cfish_super_method(klass, CFISH_Obj_Destroy_OFFSET);\n" |
| " super_destroy(self);\n" |
| " }\n" |
| "}\n" |
| "\n" |
| "#define CFISH_SUPER_DESTROY(_self, _class) \\\n" |
| " cfish_super_destroy(_self, _class)\n" |
| "\n" |
| "extern CFISH_VISIBLE cfish_Obj*\n" |
| "cfish_inc_refcount(void *vself);\n" |
| "\n" |
| "/** NULL-safe invocation invocation of `cfish_inc_refcount`.\n" |
| " *\n" |
| " * @return NULL if `self` is NULL, otherwise the return value\n" |
| " * of `cfish_inc_refcount`.\n" |
| " */\n" |
| "static CFISH_INLINE cfish_Obj*\n" |
| "cfish_incref(void *vself) {\n" |
| " if (vself != NULL) { return cfish_inc_refcount(vself); }\n" |
| " else { return NULL; }\n" |
| "}\n" |
| "\n" |
| "#define CFISH_INCREF(_self) cfish_incref(_self)\n" |
| "#define CFISH_INCREF_NN(_self) cfish_inc_refcount(_self)\n" |
| "\n" |
| "extern CFISH_VISIBLE uint32_t\n" |
| "cfish_dec_refcount(void *vself);\n" |
| "\n" |
| "/** NULL-safe invocation of `cfish_dec_refcount`.\n" |
| " *\n" |
| " * @return NULL if `self` is NULL, otherwise the return value\n" |
| " * of `cfish_dec_refcount`.\n" |
| " */\n" |
| "static CFISH_INLINE uint32_t\n" |
| "cfish_decref(void *vself) {\n" |
| " if (vself != NULL) { return cfish_dec_refcount(vself); }\n" |
| " else { return 0; }\n" |
| "}\n" |
| "\n" |
| "#define CFISH_DECREF(_self) cfish_decref(_self)\n" |
| "#define CFISH_DECREF_NN(_self) cfish_dec_refcount(_self)\n" |
| "\n" |
| "extern CFISH_VISIBLE uint32_t\n" |
| "cfish_get_refcount(void *vself);\n" |
| "\n" |
| "#define CFISH_REFCOUNT_NN(_self) \\\n" |
| " cfish_get_refcount(_self)\n" |
| "\n" |
| "/* Flags for internal use. */\n" |
| "#define CFISH_fREFCOUNTSPECIAL 0x00000001\n" |
| "#define CFISH_fFINAL 0x00000002\n" |
| ; |
| const char *cfish_defs_2 = |
| "#ifdef CFISH_USE_SHORT_NAMES\n" |
| " #define UNUSED_VAR CFISH_UNUSED_VAR\n" |
| " #define UNREACHABLE_RETURN CFISH_UNREACHABLE_RETURN\n" |
| " #define METHOD_PTR CFISH_METHOD_PTR\n" |
| " #define SUPER_METHOD_PTR CFISH_SUPER_METHOD_PTR\n" |
| " #define SUPER_DESTROY(_self, _class) CFISH_SUPER_DESTROY(_self, _class)\n" |
| " #define INCREF(_self) CFISH_INCREF(_self)\n" |
| " #define INCREF_NN(_self) CFISH_INCREF_NN(_self)\n" |
| " #define DECREF(_self) CFISH_DECREF(_self)\n" |
| " #define DECREF_NN(_self) CFISH_DECREF_NN(_self)\n" |
| " #define REFCOUNT_NN(_self) CFISH_REFCOUNT_NN(_self)\n" |
| "#endif\n" |
| "\n"; |
| |
| char *extra_defs; |
| char *extra_includes; |
| if (CFCParcel_is_cfish(parcel)) { |
| const char *spec_typedefs = CFCBindSpecs_get_typedefs(); |
| extra_defs = CFCUtil_sprintf("%s%s%s", cfish_defs_1, spec_typedefs, |
| cfish_defs_2); |
| extra_includes = CFCUtil_strdup(cfish_includes); |
| } |
| else { |
| extra_defs = CFCUtil_strdup(""); |
| extra_includes = CFCUtil_strdup(""); |
| |
| // Include parcel.h of prerequisite parcels. |
| CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(parcel); |
| for (size_t i = 0; prereq_parcels[i]; ++i) { |
| const char *prereq_prefix |
| = CFCParcel_get_prefix(prereq_parcels[i]); |
| extra_includes = CFCUtil_cat(extra_includes, "#include \"", |
| prereq_prefix, "parcel.h\"\n", NULL); |
| } |
| FREEMEM(prereq_parcels); |
| } |
| |
| const char pattern[] = |
| "%s\n" |
| "#ifndef CFISH_%sPARCEL_H\n" |
| "#define CFISH_%sPARCEL_H 1\n" |
| "\n" |
| "#ifdef __cplusplus\n" |
| "extern \"C\" {\n" |
| "#endif\n" |
| "\n" |
| "%s" // Extra includes. |
| "\n" |
| "#ifdef %s\n" |
| " #define %sVISIBLE CFISH_EXPORT\n" |
| "#else\n" |
| " #define %sVISIBLE CFISH_IMPORT\n" |
| "#endif\n" |
| "\n" |
| "%s" // Typedefs. |
| "\n" |
| "%s" // Class singletons. |
| "\n" |
| "%s" // Extra definitions. |
| "%sVISIBLE void\n" |
| "%sbootstrap_internal(int force);\n" |
| "\n" |
| "%sVISIBLE void\n" |
| "%sbootstrap_parcel(void);\n" |
| "\n" |
| "void\n" |
| "%sinit_parcel(void);\n" |
| "\n" |
| "#ifdef __cplusplus\n" |
| "}\n" |
| "#endif\n" |
| "\n" |
| "#endif /* CFISH_%sPARCEL_H */\n" |
| "\n" |
| "%s\n" |
| "\n"; |
| char *file_content |
| = CFCUtil_sprintf(pattern, self->c_header, PREFIX, PREFIX, |
| extra_includes, privacy_sym, PREFIX, PREFIX, |
| typedefs, class_decls, extra_defs, PREFIX, prefix, |
| PREFIX, prefix, prefix, PREFIX, self->c_footer); |
| |
| // Unlink then write file. |
| const char *inc_dest = CFCHierarchy_get_include_dest(hierarchy); |
| char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sparcel.h", inc_dest, |
| prefix); |
| remove(filepath); |
| CFCUtil_write_file(filepath, file_content, strlen(file_content)); |
| FREEMEM(filepath); |
| |
| FREEMEM(typedefs); |
| FREEMEM(class_decls); |
| FREEMEM(extra_defs); |
| FREEMEM(extra_includes); |
| FREEMEM(file_content); |
| } |
| |
| static void |
| S_write_parcel_c(CFCBindCore *self, CFCParcel *parcel) { |
| CFCHierarchy *hierarchy = self->hierarchy; |
| const char *prefix = CFCParcel_get_prefix(parcel); |
| |
| // Aggregate C code for the parcel. |
| char *privacy_syms = CFCUtil_strdup(""); |
| char *includes = CFCUtil_strdup(""); |
| char *c_data = CFCUtil_strdup(""); |
| CFCBindSpecs *specs = CFCBindSpecs_new(); |
| CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); |
| |
| for (int i = 0; ordered[i] != NULL; i++) { |
| CFCClass *klass = ordered[i]; |
| const char *class_prefix = CFCClass_get_prefix(klass); |
| if (strcmp(class_prefix, prefix) != 0) { continue; } |
| |
| const char *include_h = CFCClass_include_h(klass); |
| includes = CFCUtil_cat(includes, "#include \"", include_h, |
| "\"\n", NULL); |
| |
| CFCBindClass *class_binding = CFCBindClass_new(klass); |
| |
| char *class_c_data = CFCBindClass_to_c_data(class_binding); |
| c_data = CFCUtil_cat(c_data, class_c_data, "\n", NULL); |
| FREEMEM(class_c_data); |
| |
| CFCBindSpecs_add_class(specs, klass); |
| |
| const char *privacy_sym = CFCClass_privacy_symbol(klass); |
| privacy_syms = CFCUtil_cat(privacy_syms, "#define ", |
| privacy_sym, "\n", NULL); |
| |
| CFCBase_decref((CFCBase*)class_binding); |
| } |
| |
| char *spec_defs = CFCBindSpecs_defs(specs); |
| char *spec_init_func = CFCBindSpecs_init_func_def(specs); |
| FREEMEM(ordered); |
| |
| char *prereq_bootstrap = CFCUtil_strdup(""); |
| CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(parcel); |
| for (size_t i = 0; prereq_parcels[i]; ++i) { |
| const char *prereq_prefix = CFCParcel_get_prefix(prereq_parcels[i]); |
| prereq_bootstrap = CFCUtil_cat(prereq_bootstrap, " ", prereq_prefix, |
| "bootstrap_internal(0);\n", NULL); |
| } |
| FREEMEM(prereq_parcels); |
| |
| char pattern[] = |
| "%s\n" |
| "\n" |
| "#include <stdio.h>\n" |
| "#include <stdlib.h>\n" |
| "\n" |
| "%s" |
| "\n" |
| "#include \"Clownfish/Class.h\"\n" // Needed for bootstrap. |
| "#include \"Clownfish/Err.h\"\n" // Needed for abstract methods. |
| "%s\n" |
| "\n" |
| "%s\n" |
| "\n" |
| "/* ClassSpec and MethSpec structs for initialization.\n" |
| " */\n" |
| "\n" |
| "%s" // spec_defs |
| "\n" |
| "/* Code to initialize ClassSpec and MethSpec structs.\n" |
| " */\n" |
| "\n" |
| "%s" // spec_init_func |
| "\n" |
| "void\n" |
| "%sbootstrap_internal(int force) {\n" |
| " static int bootstrapped = 0;\n" |
| " if (bootstrapped && !force) { return; }\n" |
| " S_bootstrap_specs();\n" |
| " %sinit_parcel();\n" |
| " bootstrapped = 1;\n" |
| "}\n" |
| "\n" |
| "void\n" |
| "%sbootstrap_parcel() {\n" |
| "%s" // Bootstrap prerequisite parcels. |
| " %sbootstrap_internal(0);\n" |
| "}\n" |
| "\n" |
| "%s\n"; |
| char *file_content |
| = CFCUtil_sprintf(pattern, self->c_header, privacy_syms, includes, |
| c_data, spec_defs, spec_init_func, prefix, prefix, |
| prefix, prereq_bootstrap, prefix, self->c_footer); |
| |
| // Unlink then open file. |
| const char *src_dest = CFCHierarchy_get_source_dest(hierarchy); |
| char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%sparcel.c", src_dest, |
| prefix); |
| remove(filepath); |
| CFCUtil_write_file(filepath, file_content, strlen(file_content)); |
| FREEMEM(filepath); |
| |
| CFCBase_decref((CFCBase*)specs); |
| FREEMEM(privacy_syms); |
| FREEMEM(includes); |
| FREEMEM(c_data); |
| FREEMEM(spec_defs); |
| FREEMEM(spec_init_func); |
| FREEMEM(prereq_bootstrap); |
| FREEMEM(file_content); |
| } |
| |
| /* Write the "cfish_platform.h" header file, which contains platform-specific |
| * definitions. |
| */ |
| static void |
| S_write_platform_h(CFCBindCore *self) { |
| char *feature_defs = S_charmony_feature_defines(); |
| char *string_defs = S_charmony_string_defines(); |
| char *stdbool_defs = S_charmony_stdbool_defines(); |
| char *stdint_defs = S_charmony_stdint_defines(); |
| char *alloca_defs = S_charmony_alloca_defines(); |
| |
| const char pattern[] = |
| "%s" |
| "\n" |
| "#ifndef CFISH_PLATFORM_H\n" |
| "#define CFISH_PLATFORM_H 1\n" |
| "\n" |
| "#ifdef __cplusplus\n" |
| "extern \"C\" {\n" |
| "#endif\n" |
| "\n" |
| "%s" |
| "%s" |
| "\n" |
| "%s" |
| "%s" |
| "\n" |
| "%s" |
| "\n" |
| "#ifdef __cplusplus\n" |
| "}\n" |
| "#endif\n" |
| "\n" |
| "#endif /* CFISH_PLATFORM_H */\n" |
| "\n" |
| "%s" |
| "\n"; |
| char *file_content |
| = CFCUtil_sprintf(pattern, self->c_header, feature_defs, string_defs, |
| stdbool_defs, stdint_defs, alloca_defs, |
| self->c_footer); |
| |
| // Unlink then write file. |
| const char *inc_dest = CFCHierarchy_get_include_dest(self->hierarchy); |
| char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "cfish_platform.h", |
| inc_dest); |
| remove(filepath); |
| CFCUtil_write_file(filepath, file_content, strlen(file_content)); |
| FREEMEM(filepath); |
| |
| FREEMEM(feature_defs); |
| FREEMEM(string_defs); |
| FREEMEM(stdbool_defs); |
| FREEMEM(stdint_defs); |
| FREEMEM(alloca_defs); |
| FREEMEM(file_content); |
| } |
| |
| static char* |
| S_charmony_feature_defines() { |
| char *defines = CFCUtil_strdup(""); |
| |
| #ifdef CHY_LITTLE_END |
| // Needed by NumberUtils.cfh. |
| defines = CFCUtil_cat(defines, "#define CFISH_LITTLE_END\n", NULL); |
| #endif |
| #ifdef CHY_BIG_END |
| // Needed by NumberUtils.cfh. |
| defines = CFCUtil_cat(defines, "#define CFISH_BIG_END\n", NULL); |
| #endif |
| #ifdef CHY_HAS_FUNC_MACRO |
| // Needed by Err.cfh. |
| defines = CFCUtil_cat(defines, "#define CFISH_HAS_FUNC_MACRO\n", NULL); |
| #endif |
| #ifdef CHY_HAS_VARIADIC_MACROS |
| // Needed by Err.cfh. |
| defines = CFCUtil_cat(defines, "#define CFISH_HAS_VARIADIC_MACROS\n", |
| NULL); |
| #endif |
| #ifdef CHY_HAS_ISO_VARIADIC_MACROS |
| // Needed by Err.cfh. |
| defines = CFCUtil_cat(defines, "#define CFISH_HAS_ISO_VARIADIC_MACROS\n", |
| NULL); |
| #endif |
| #ifdef CHY_HAS_GNUC_VARIADIC_MACROS |
| // Needed by Err.cfh. |
| defines = CFCUtil_cat(defines, "#define CFISH_HAS_GNUC_VARIADIC_MACROS\n", |
| NULL); |
| #endif |
| |
| return defines; |
| } |
| |
| static char* |
| S_charmony_string_defines() { |
| const char *pattern = |
| "#define CFISH_INLINE %s\n" |
| "#define CFISH_EXPORT %s\n" |
| "#define CFISH_IMPORT %s\n" |
| "#define CFISH_SIZEOF_CHAR %s\n" |
| "#define CFISH_SIZEOF_SHORT %s\n" |
| "#define CFISH_SIZEOF_INT %s\n" |
| "#define CFISH_SIZEOF_LONG %s\n" |
| "#define CFISH_SIZEOF_SIZE_T %s\n" |
| "#define CFISH_FUNC_MACRO %s\n" |
| "#define CFISH_U64_TO_DOUBLE(x) %s\n"; |
| char *defines |
| = CFCUtil_sprintf(pattern, |
| XSTRING(CHY_INLINE), |
| XSTRING(CHY_EXPORT), |
| XSTRING(CHY_IMPORT), |
| XSTRING(CHY_SIZEOF_CHAR), |
| XSTRING(CHY_SIZEOF_SHORT), |
| XSTRING(CHY_SIZEOF_INT), |
| XSTRING(CHY_SIZEOF_LONG), |
| XSTRING(CHY_SIZEOF_SIZE_T), |
| XSTRING(CHY_FUNC_MACRO), |
| XSTRING(CHY_U64_TO_DOUBLE(x))); |
| |
| return defines; |
| } |
| |
| static char* |
| S_charmony_stdbool_defines() { |
| #ifdef CHY_HAS_STDBOOL_H |
| const char *defines = "#include <stdbool.h>\n"; |
| #else |
| const char *defines = |
| "#if (!defined(__cplusplus) && !defined(CFISH_HAS_STDBOOL))\n" |
| " typedef int bool;\n" |
| " #ifndef true\n" |
| " #define true 1\n" |
| " #endif\n" |
| " #ifndef false\n" |
| " #define false 0\n" |
| " #endif\n" |
| "#endif\n"; |
| #endif |
| |
| return CFCUtil_strdup(defines); |
| } |
| |
| static char* |
| S_charmony_stdint_defines() { |
| #ifdef CHY_HAS_STDINT_H |
| return CFCUtil_strdup("#include <stdint.h>\n"); |
| #else |
| const char *pattern = |
| "#ifndef CFISH_HAS_STDINT\n" |
| " typedef %s int8_t;\n" |
| " typedef %s uint8_t;\n" |
| " typedef %s int16_t;\n" |
| " typedef %s uint16_t;\n" |
| " typedef %s int32_t;\n" |
| " typedef %s uint32_t;\n" |
| " typedef %s int64_t;\n" |
| " typedef %s uint64_t;\n" |
| "#endif\n"; |
| return CFCUtil_sprintf(pattern, |
| XSTRING(CHY_INT8_T), XSTRING(CHY_UINT8_T), |
| XSTRING(CHY_INT16_T), XSTRING(CHY_UINT16_T), |
| XSTRING(CHY_INT32_T), XSTRING(CHY_UINT32_T), |
| XSTRING(CHY_INT64_T), XSTRING(CHY_UINT64_T)); |
| #endif |
| } |
| |
| static char* |
| S_charmony_alloca_defines() { |
| char *defines = CFCUtil_strdup(""); |
| |
| #if defined(CHY_HAS_ALLOCA_H) |
| defines = CFCUtil_cat(defines, "#include <alloca.h>\n", NULL); |
| #elif defined(CHY_HAS_MALLOC_H) |
| defines = CFCUtil_cat(defines, "#include <malloc.h>\n", NULL); |
| #elif defined(CHY_ALLOCA_IN_STDLIB_H) |
| defines = CFCUtil_cat(defines, "#include <stdlib.h>\n", NULL); |
| #endif |
| |
| defines = CFCUtil_cat(defines, "#define cfish_alloca ", |
| XSTRING(chy_alloca), "\n", NULL); |
| |
| return defines; |
| } |
| |
| void |
| CFCBindCore_copy_headers(CFCBindCore *self, const char *dest_dir) { |
| char *default_dest = NULL; |
| |
| if (dest_dir == NULL || dest_dir[0] == '\0') { |
| default_dest = CFCUtil_sprintf("%s" CHY_DIR_SEP "share" CHY_DIR_SEP |
| "clownfish" CHY_DIR_SEP "include", |
| CFCHierarchy_get_dest(self->hierarchy)); |
| dest_dir = default_dest; |
| } |
| |
| /* Copy .cfp files. */ |
| CFCParcel **parcels = CFCParcel_all_parcels(); |
| for (size_t i = 0; parcels[i] != NULL; i++) { |
| CFCParcel *parcel = parcels[i]; |
| if (CFCParcel_included(parcel) || !CFCParcel_is_installed(parcel)) { |
| continue; |
| } |
| |
| const char *source_path = CFCParcel_get_cfp_path(parcel); |
| const char *parcel_name = CFCParcel_get_name(parcel); |
| CFCVersion *version = CFCParcel_get_version(parcel); |
| const char *vstring = CFCVersion_get_vstring(version); |
| |
| char *dest_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s" CHY_DIR_SEP |
| "%s" CHY_DIR_SEP "parcel.json", |
| dest_dir, parcel_name, vstring); |
| |
| size_t len = 0; |
| char *content = CFCUtil_slurp_text(source_path, &len); |
| CFCUtil_write_file(dest_path, content, len); |
| |
| FREEMEM(content); |
| FREEMEM(dest_path); |
| } |
| |
| /* Copy .cfh files. */ |
| CFCFile **files = CFCHierarchy_files(self->hierarchy); |
| for (size_t i = 0; files[i] != NULL; i++) { |
| CFCFile *file = files[i]; |
| if (CFCFile_included(file)) { continue; } |
| CFCParcel *parcel = CFCFile_get_parcel(file); |
| if (!CFCParcel_is_installed(parcel)) { continue; } |
| |
| const char *source_path = CFCFile_get_path(file); |
| const char *parcel_name = CFCParcel_get_name(parcel); |
| CFCVersion *version = CFCParcel_get_version(parcel); |
| const char *vstring = CFCVersion_get_vstring(version); |
| const char *path_part = CFCFile_get_path_part(file); |
| |
| char *dest_path = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s" CHY_DIR_SEP |
| "%s" CHY_DIR_SEP "%s.cfh", dest_dir, |
| parcel_name, vstring, path_part); |
| |
| size_t len = 0; |
| char *content = CFCUtil_slurp_text(source_path, &len); |
| CFCUtil_write_file(dest_path, content, len); |
| |
| FREEMEM(content); |
| FREEMEM(dest_path); |
| } |
| |
| FREEMEM(default_dest); |
| } |
| |
| void |
| CFCBindCore_write_host_data_json(CFCBindCore *self, const char *dest_dir, |
| const char *host_lang) { |
| CFCParcel **parcels = CFCParcel_all_parcels(); |
| |
| for (size_t i = 0; parcels[i] != NULL; i++) { |
| CFCParcel *parcel = parcels[i]; |
| if (!CFCParcel_included(parcel) && CFCParcel_is_installed(parcel)) { |
| S_write_host_data_json(self, parcel, dest_dir, host_lang); |
| } |
| } |
| } |
| |
| static void |
| S_write_host_data_json(CFCBindCore *self, CFCParcel *parcel, |
| const char *dest_dir, const char *host_lang) { |
| const char *prefix = CFCParcel_get_prefix(parcel); |
| const char *parcel_name = CFCParcel_get_name(parcel); |
| CFCVersion *version = CFCParcel_get_version(parcel); |
| const char *vstring = CFCVersion_get_vstring(version); |
| char *json_pairs = CFCUtil_strdup(""); |
| |
| const char *host_module_name = CFCParcel_get_host_module_name(parcel); |
| if (host_module_name != NULL) { |
| const char *pattern = " \"host_module\": \"%s\""; |
| char *pair = CFCUtil_sprintf(pattern, host_module_name); |
| json_pairs = CFCUtil_cat(json_pairs, pair, NULL); |
| FREEMEM(pair); |
| } |
| |
| char *classes_json = CFCUtil_strdup(""); |
| CFCClass **ordered = CFCHierarchy_ordered_classes(self->hierarchy); |
| |
| for (size_t i = 0; ordered[i] != NULL; i++) { |
| CFCClass *klass = ordered[i]; |
| const char *class_prefix = CFCClass_get_prefix(klass); |
| if (strcmp(class_prefix, prefix) != 0) { continue; } |
| |
| CFCBindClass *class_binding = CFCBindClass_new(klass); |
| |
| char *class_json = CFCBindClass_host_data_json(class_binding); |
| if (class_json[0] != '\0') { |
| const char *sep = classes_json[0] == '\0' ? "" : ",\n"; |
| classes_json = CFCUtil_cat(classes_json, sep, class_json, NULL); |
| } |
| |
| FREEMEM(class_json); |
| CFCBase_decref((CFCBase*)class_binding); |
| } |
| FREEMEM(ordered); |
| |
| if (classes_json[0] != '\0') { |
| const char *pattern = |
| " \"classes\": {\n" |
| "%s\n" |
| " }"; |
| char *pair = CFCUtil_sprintf(pattern, classes_json); |
| const char *sep = json_pairs[0] == '\0' ? "" : ",\n"; |
| json_pairs = CFCUtil_cat(json_pairs, sep, pair, NULL); |
| FREEMEM(pair); |
| } |
| |
| char *filepath = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s" CHY_DIR_SEP "%s" |
| CHY_DIR_SEP "parcel_%s.json", dest_dir, |
| parcel_name, vstring, host_lang); |
| remove(filepath); |
| |
| if (json_pairs[0] != '\0') { |
| const char *pattern = |
| "{\n" |
| "%s\n" |
| "}\n"; |
| char *json = CFCUtil_sprintf(pattern, json_pairs); |
| CFCUtil_write_file(filepath, json, strlen(json)); |
| FREEMEM(json); |
| } |
| |
| FREEMEM(filepath); |
| FREEMEM(classes_json); |
| FREEMEM(json_pairs); |
| } |
| |
| |