#include #include #include #include #include #include #include #include #include "entity.h" static char *codedir; static int link_in_so (ENode * node, gchar * lafile); static void import_in (GModule * module, gchar * function); static char *stdheaders = " \ #include \n \ #include \n \ #include \n \ #include \n \ "; static GHashTable *c_functions_ht = NULL; static gint check_checksum (char *obj, char *code) { gchar *checksumfile = g_strconcat (codedir, "/.sum/", obj, NULL); unsigned int old_hash = 0; unsigned int new_hash = x31_hash (code); FILE *file_handle = NULL; /* if there is a hash, read it in and check agains the new hash */ file_handle = fopen (checksumfile, "r"); if (file_handle) { fscanf (file_handle, "%u", &old_hash); fclose (file_handle); } g_free (checksumfile); /* if they are the same ...do nothing but return true */ if (old_hash == new_hash) return (TRUE); return (FALSE); } static void write_checksum (char *obj, char *code) { gchar *checksumfile = g_strconcat (codedir, "/.sum/", obj, NULL); unsigned int new_hash = x31_hash (code); FILE *file_handle = NULL; file_handle = fopen (checksumfile, "w+"); if (file_handle) { fprintf (file_handle, "%u", new_hash); fclose (file_handle); } g_free (checksumfile); } gchar * c_compile_str_get (ENode * node, char *tag) { char *str = NULL; char *temp; GSList *list; ENode *child; /* check current node for the tag first */ str = enode_attrib_str (node, tag, NULL); /* Tag was not found, now check for children */ if (!str) { for (list = node->children; list; list = list->next) { child = (ENode *) list->data; if (strcmp (child->element->str, tag) != 0 || !child->data) { continue; } temp = g_strconcat (child->data->str, str, NULL); if (str) { g_free (str); } str = temp; } } /* Make sure the string has "something" */ if (!str) { str = ""; } return (str); } /* Render all the c-code tags. * Code that has been modified will be * recompiled and relinked into * .so files. These .so are then dlopen'ed * and functions that have * been exported to entity using the * tag will then * added to the c_functions_ht so that they can be called * from entity. */ static void c_node_render (ENode * node) { gchar *obj; /* Object name, append .so for the lib name. */ gchar *includes; /* Text inside of . */ gchar *libs; /* Text inside of . */ gchar *obj_cmd; /* Commands that are ran to compile the c. */ gchar *so_cmd; gchar *lafile; /* Path to the outputed .la library info file */ gchar *data; FILE *fp = NULL; /* Check to make sure we have a valid node, with data */ if (!node || !node->data) { return; } obj = enode_attrib_str (node, "object", NULL); if (!obj) { obj = "libdefault"; /* This will be really SLOW!!! because each * new */ } /* will have to be compiled. FIXME */ else { obj = g_strconcat ("lib", obj, NULL); /* libtool needs the object * to start with a lib */ } includes = c_compile_str_get (node, "c-includes"); libs = c_compile_str_get (node, "c-libs"); /* This is the data that is checksummed, we need all od this so that * * the relink and recompile happen if only the data->str, NULL); lafile = g_strconcat (codedir, "/", obj, ".la", NULL); if (check_checksum (obj, data) == FALSE) { char *tempfile = NULL; /* File created to hold the c code. */ int compile_ok = TRUE; tempfile = g_strconcat (codedir, "/entity.c", NULL); /* Open temp file. */ if (tempfile) { fp = fopen (tempfile, "w"); } if (!fp) { g_warning ("Unable to open temp file '%s' for writing: %s", tempfile, g_strerror (errno)); return; } fprintf (fp, "%s", stdheaders); fprintf (fp, "%s", node->data->str); fclose (fp); /* Build the object compile string */ obj_cmd = g_strconcat (DATADIR, "/libtool --mode=compile ", COMPILER, " ", includes, " `entity-config --cflags` ", tempfile, " -c -o ", codedir, "/.objects/", obj, ".lo", NULL); /* Build the so linking string */ so_cmd = g_strconcat (DATADIR, "/libtool --mode=link ", COMPILER, " ", libs, " ", "-avoid-version -module ", codedir, "/.objects/", obj, ".lo", " -rpath /usr/lib -o ", lafile, NULL); /* Rebuild if checksum is different */ EDEBUG (("c-embed", "Executing libtool command: %s", obj_cmd)); if (system (obj_cmd)) { compile_ok = FALSE; g_warning ("C-code was not recompiled! %s\n", obj_cmd); } else { /* on to link stage */ EDEBUG (("c-embed", "Executing libtool command: %s", so_cmd)); if (system (so_cmd)) { compile_ok = FALSE; g_warning ("C-code was not relinked! %s\n", so_cmd); } } /* Only write out checksum if the compile was ok */ if (compile_ok == TRUE) write_checksum (obj, data); g_free (obj_cmd); g_free (so_cmd); g_free (tempfile); } g_free (data); link_in_so (node, lafile); g_free (lafile); } static int link_in_so (ENode * node, gchar * lafile) { GModule *module; void (*init)(void); /* The init function. */ gchar buf[2048]; FILE *file_handle = NULL; int err = FALSE; gchar *modpath; gchar *dlname; gchar *command; gchar **segments = NULL; dlname = eutils_module_dlname (lafile); if (!dlname) { g_warning ("Unable to deduce shared object file to load, giving up!"); return (1); } modpath = g_strconcat (codedir, "/.libs/", dlname, NULL); g_free (dlname); EDEBUG (("c-embed", "Loading object '%s' from path '%s'", dlname, modpath)); module = g_module_open (modpath, G_MODULE_BIND_LAZY); if (!module) { g_warning ("Error loading dynamic library '%s': %s\n", modpath, g_module_error()); return (1); } /* do a popen and hand it a nm command to generate function names */ command = g_strconcat ("nm -p ", modpath, NULL); EDEBUG (("c-embed", "Executing command: '%s'", command)); file_handle = popen (command, "r"); g_free (command); g_free (modpath); /* check the file handle */ if (file_handle) { while (FALSE == err) { if (NULL != fgets (buf, 2048, file_handle)) { /* g_strchomp operates on string in-place */ g_strchomp (buf); segments = g_strsplit (buf, " ", 0); if (segments && segments[1] && (0 == strcmp (segments[1], "T")) && segments[2] && segments[2] != '\0') { import_in (module, segments[2]); /* import the module * from the .so */ } if (segments) g_strfreev (segments); } else { err = TRUE; } } (void) pclose (file_handle); } /* Call the entity_init function in the library if it's there. */ if (g_module_symbol (module, "entity_c_init", (gpointer *) &init) ) { EDEBUG (("c-embed", "running 'entity_c_init' in C-code.\n")); enode_call_reference_push (node); init (); enode_call_reference_pop (); } return (0); } /* Imports in a module from a .so */ void import_in (GModule * module, gchar * function) { void (*func) (GSList *); if (function) { EDEBUG (("c-embed", "importing in function in '%s'", function)); /* On some systems, all symbols start with _ */ if (function[0] == '_') function++; g_module_symbol (module, function, (void **) &func); if (func) { g_hash_table_insert (c_functions_ht, g_strdup (function), func); } else { g_warning ("nm returned function %s, but theres no such symbol", function); } } } EBuf * c_function_execute (ENode * calling_node, gchar * function, GSList * args) { GSList *tmp; EBuf *retval = NULL; EBuf *(*funct) (ENode *, GSList *); funct = g_hash_table_lookup (c_functions_ht, function); if (funct) { enode_call_reference_push (calling_node); retval = funct (calling_node, args); enode_call_reference_pop (); } else { g_warning ("While trying to execute C function %s: %s not defined.\n", function, function); } /* Free argument list */ tmp = args; while (tmp) { LangArg *arg = tmp->data; enode_call_free_arg (arg); tmp = tmp->next; } return (retval); } #ifdef STATIC_C void c_init (RendererFlags flags) #else void renderer_init (RendererFlags flags) #endif { Element *element; char *sumdir; char *libdir; c_functions_ht = g_hash_table_new (g_str_hash, g_str_equal); if (flags & RENDERER_INIT) { codedir = g_strconcat (g_get_home_dir (), "/.entity/c-code", NULL); sumdir = g_strconcat (codedir, "/.sum", NULL); libdir = g_strconcat (codedir, "/.objects", NULL); if (mkdir (codedir, 0750) == -1 && errno != EEXIST) { g_warning ("Cant create %s, no c-code tags can be rendered!\n", codedir); } if (mkdir (sumdir, 0750) == -1 && errno != EEXIST) { g_warning ("Cant create %s, no c-code tags can be rendered!\n", sumdir); } if (mkdir (libdir, 0750) == -1 && errno != EEXIST) { g_warning ("Cant create %s, no c-code tags can be rendered!\n", libdir); } g_free (sumdir); g_free (libdir); } if (flags & RENDERER_REGISTER) { element = g_new0 (Element, 1); element->render_func = c_node_render; element->destroy_func = NULL; element->description = "Embed C code directly into an Entity application."; element->tag = "c-code"; element_register (element); language_register ("c-code", c_function_execute); language_register ("C", c_function_execute); language_register ("c", c_function_execute); } }