#include <unistd.h> /* getpid() */
#include <stdio.h>
#include "gskghelpers.h"
#include "gskerror.h"
#include "gskmodule.h"
#include "compile-info.h"
struct _GskCompileContext
{
char *tmp_dir;
char *cc, *ld;
GString *cflags;
GString *ldflags;
GPtrArray *packages;
char *package_cflags, *package_ldflags;
gboolean gdb_support;
gboolean verbose;
};
/**
* gsk_compile_context_new:
*
* Create a new compilation context.
*
* returns: the new compilation-context.
*/
GskCompileContext *gsk_compile_context_new ()
{
GskCompileContext *rv = g_new (GskCompileContext, 1);
rv->tmp_dir = NULL;
rv->cc = g_strdup (GSK_CC);
rv->ld = g_strdup (GSK_LD_SHLIB);
rv->cflags = g_string_new (GSK_CFLAGS " " GSK_COMPILE_ONLY_FLAG);
rv->ldflags = g_string_new (GSK_LD_SHLIB_FLAGS);
rv->packages = g_ptr_array_new ();
rv->package_cflags = NULL;
rv->package_ldflags = NULL;
rv->gdb_support = FALSE;
rv->verbose = FALSE;
return rv;
}
/**
* gsk_compile_context_add_cflags:
* @context: the compilation context.
* @flags: a space-separated list of compiler flags.
* This will be passed through the shell.
*
* Add compiler flags that you want used in
* this compilation-context.
*/
void
gsk_compile_context_add_cflags (GskCompileContext *context,
const char *flags)
{
g_string_append_c (context->cflags, ' ');
g_string_append (context->cflags, flags);
}
/**
* gsk_compile_context_add_ldflags:
* @context: the compilation context.
* @flags: a space-separated list of linker flags.
* This will be passed through the shell.
*
* Add linker flags that you want used in
* this compilation-context.
*/
void gsk_compile_context_add_ldflags(GskCompileContext*context,
const char *flags)
{
g_string_append_c (context->ldflags, ' ');
g_string_append (context->ldflags, flags);
}
/**
* gsk_compile_context_add_pkg:
* @context: the compilation context.
* @pkg: the name of a library as known by pkgconfig(1).
*
* Add a package-dependency for this compilation context.
*
* TODO error-handling.
*/
void gsk_compile_context_add_pkg (GskCompileContext*context,
const char *pkg)
{
g_ptr_array_add (context->packages, g_strdup (pkg));
if (context->package_cflags)
{
g_free (context->package_cflags);
context->package_cflags = NULL;
}
if (context->package_ldflags)
{
g_free (context->package_ldflags);
context->package_ldflags = NULL;
}
}
/**
* gsk_compile_context_set_tmp_dir:
* @context: the compilation context.
* @tmp_dir: the temporary directory to use.
*
* Set the directory to use for temporary files.
*/
void gsk_compile_context_set_tmp_dir(GskCompileContext*context,
const char *tmp_dir)
{
char *t = g_strdup (tmp_dir);
g_free (context->tmp_dir);
context->tmp_dir = t;
}
/**
* gsk_compile_context_set_gdb:
* @context: the compilation context.
* @support: whether to support gdb by not immediately deleting temporary files.
*
* Set whether gdb will be supported on shared-libraries
* created with this context.
* The default is FALSE.
*/
void gsk_compile_context_set_gdb (GskCompileContext *context,
gboolean support)
{
context->gdb_support = support;
}
/**
* gsk_compile_context_set_verbose:
* @context: the compilation context.
* @support: whether to support be verbose.
*
* Set whether to be verbose.
* When the context is verbose, compilation and link commands
* are printed out to stderr, with "compiling: " and "linking: " prefixes.
*
* The default is FALSE.
*/
void gsk_compile_context_set_verbose(GskCompileContext *context,
gboolean support)
{
context->verbose = support;
}
/**
* gsk_compile_context_free:
* @context: the compilation context to free.
*
* Free memory used by the compilation context.
*/
void
gsk_compile_context_free (GskCompileContext *context)
{
g_free (context->tmp_dir);
g_free (context->cc);
g_free (context->ld);
g_string_free (context->cflags, TRUE);
g_string_free (context->ldflags, TRUE);
gsk_g_ptr_array_foreach (context->packages, (GFunc) g_free, NULL);
g_ptr_array_free (context->packages, TRUE);
g_free (context->package_cflags);
g_free (context->package_ldflags);
g_free (context);
}
struct _GskModule
{
GModule *module;
guint ref_count;
char **files_to_kill;
};
static gboolean
run_pkg_config (GskCompileContext *context,
const char *prg_option,
char **flags_out,
GError **error)
{
GString *cmd_str;
GString *str;
guint i;
FILE *fp;
char buf[4096];
int pclose_rv;
cmd_str = g_string_new (GSK_PKGCONFIG);
g_string_append_printf (cmd_str, " --cflags");
for (i = 0; i < context->packages->len; i++)
g_string_append_printf (cmd_str, " %s",
(char*)(context->packages->pdata[i]));
str = g_string_new ("");
fp = popen (cmd_str->str, "r");
while (fgets (buf, sizeof (buf), fp) != NULL)
g_string_append (str, buf);
pclose_rv = pclose (fp);
if (pclose_rv < 0)
g_error ("error running pkg-config");
if (pclose_rv != 0)
{
if (pclose_rv < 255)
g_set_error (error, GSK_G_ERROR_DOMAIN, GSK_ERROR_COMPILE,
"pkg-config died with signal %u", pclose_rv);
else
g_set_error (error, GSK_G_ERROR_DOMAIN, GSK_ERROR_COMPILE,
"pkg-config returned exit status %u", pclose_rv);
return FALSE;
}
g_strstrip (str->str);
*flags_out = g_strdup (str->str);
g_string_free (str, TRUE);
g_string_free (cmd_str, TRUE);
return TRUE;
}
static gboolean
ensure_pkg_info_ok (GskCompileContext *context,
GError **error)
{
if (context->package_ldflags == NULL)
{
if (context->packages->len == 0)
{
context->package_cflags = g_strdup ("");
context->package_ldflags = g_strdup ("");
}
else
{
if (!run_pkg_config (context, "--cflags", &context->package_cflags, error)
|| !run_pkg_config (context, "--libs", &context->package_ldflags, error))
return FALSE;
}
}
return TRUE;
}
/**
* gsk_module_compile:
* @context: the compilation context.
* @n_sources: the number of source files to compile into the module.
* @sources: the source files.
* @flags: ...
* @delete_sources: whether to delete the source files
* as possible (depending on whether gdb support is enabled)
* @program_output: the program's output.
* @error: where to put an error if something goes wrong.
*
* Compile a collection of sources into a module.
* This will invoke the compiler n_sources times,
* and the linker once.
*
* If @delete_sources is set, then the source files
* will be deleted, but when depends on whether gdb support is enabled.
* If it is enabled, then they are deleted only once the module is closed.
*/
GskModule *
gsk_module_compile (GskCompileContext *context,
guint n_sources,
char **sources,
GModuleFlags flags,
gboolean delete_sources,
char **program_output,
GError **error)
{
GModule *module;
GskModule *rv;
GString *linker_cmd;
char *output_fname;
char **to_kill_files;
guint i;
GString *output;
char buf[4096];
/* pick unused filename for module */
{
static guint seq = 0;
for (;;)
{
output_fname = g_strdup_printf ("%s/mod-%u-%u.so",
context->tmp_dir ? context->tmp_dir
: g_get_tmp_dir (),
(guint) getpid (),
seq++);
if (!g_file_test (output_fname, G_FILE_TEST_EXISTS))
break;
g_free (output_fname);
}
}
if (!ensure_pkg_info_ok (context, error))
{
return NULL; /* TODO: cleanup */
}
linker_cmd = g_string_new (context->ld);
g_string_append_printf (linker_cmd, " %s %s -o '%s'",
context->ldflags->str,
context->package_ldflags,
output_fname);
output = g_string_new ("");
for (i = 0; i < n_sources; i++)
{
char *command = g_strdup_printf ("%s %s %s -o '%s.o' '%s' 2>&1",
context->cc,
context->cflags->str,
context->package_cflags,
sources[i], sources[i]);
FILE *fp;
int pclose_rv;
if (context->verbose)
g_printerr ("compiling: %s\n", command);
fp = popen (command, "r");
while (fgets (buf, sizeof (buf), fp) != NULL)
g_string_append (output, buf);
pclose_rv = pclose (fp);
if (pclose_rv != 0)
{
g_set_error (error,
GSK_G_ERROR_DOMAIN,
GSK_ERROR_COMPILE,
"error compiling shlib");
if (program_output)
*program_output = g_string_free (output, FALSE);
else
g_string_free (output, TRUE);
g_free (command);
return NULL;
}
g_free (command);
g_string_append_printf (linker_cmd, " '%s.o'", sources[i]);
}
/* assemble linker command */
{
FILE *fp;
if (context->verbose)
g_printerr ("linking: %s\n", linker_cmd->str);
fp = popen (linker_cmd->str, "r");
g_string_free (linker_cmd, TRUE);
linker_cmd = NULL;
while (fgets (buf, sizeof (buf), fp) != NULL)
g_string_append (output, buf);
if (pclose (fp) < 0)
{
g_set_error (error,
GSK_G_ERROR_DOMAIN,
GSK_ERROR_COMPILE,
"error linking shlib");
if (program_output)
*program_output = g_string_free (output, FALSE);
else
g_string_free (output, TRUE);
return NULL;
}
}
module = g_module_open (output_fname, flags);
if (module == NULL)
{
g_set_error (error,
GSK_G_ERROR_DOMAIN,
GSK_ERROR_OPEN_MODULE,
"error opening creating module %s: %s",
output_fname, g_module_error ());
return NULL;
}
rv = g_new (GskModule, 1);
rv->module = module;
rv->ref_count = 1;
/* make the list of files to unlink. */
{
GPtrArray *to_kill = g_ptr_array_new ();
if (delete_sources)
{
for (i = 0; i < n_sources; i++)
g_ptr_array_add (to_kill, g_strdup (sources[i]));
}
for (i = 0; i < n_sources; i++)
g_ptr_array_add (to_kill, g_strdup_printf ("%s.o", sources[i]));
g_ptr_array_add (to_kill, output_fname);
g_ptr_array_add (to_kill, NULL);
to_kill_files = (char **) g_ptr_array_free (to_kill, FALSE);
}
if (context->gdb_support)
{
rv->files_to_kill = to_kill_files;
}
else
{
/* delete files immediately */
char **at;
for (at = to_kill_files; *at; at++)
unlink (*at);
g_strfreev (to_kill_files);
rv->files_to_kill = NULL;
}
if (program_output)
*program_output = g_string_free (output, FALSE);
else
g_string_free (output, TRUE);
return rv;
}
GskModule *
gsk_module_open (const char *filename,
GModuleFlags flags,
GError **error)
{
GModule *module = g_module_open (filename, flags);
GskModule *rv;
if (module == NULL)
{
g_set_error (error,
GSK_G_ERROR_DOMAIN,
GSK_ERROR_OPEN_MODULE,
"error opening module %s: %s",
filename, g_module_error ());
return NULL;
}
rv = g_new (GskModule, 1);
rv->ref_count = 1;
rv->files_to_kill = NULL;
rv->module = module;
return rv;
}
GskModule *
gsk_module_ref (GskModule *module)
{
g_return_val_if_fail (module->ref_count > 0, module);
++(module->ref_count);
return module;
}
void
gsk_module_unref (GskModule *module)
{
g_return_if_fail (module->ref_count > 0);
if (--(module->ref_count) == 0)
{
if (module->files_to_kill)
{
char **at;
for (at = module->files_to_kill; *at; at++)
unlink (*at);
g_strfreev (module->files_to_kill);
}
g_module_close (module->module);
g_free (module);
}
}
gpointer
gsk_module_lookup (GskModule *module,
const char *symbol_name)
{
gpointer rv;
if (g_module_symbol (module->module, symbol_name, &rv))
return rv;
else
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1