2018-12-15 00:05:34 +01:00
|
|
|
/*
|
|
|
|
* GDSII-Converter
|
|
|
|
* Copyright (C) 2018 Mario Hüttel <mario.huettel@gmx.net>
|
|
|
|
*
|
|
|
|
* This file is part of GDSII-Converter.
|
|
|
|
*
|
|
|
|
* GDSII-Converter is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
|
|
* published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* GDSII-Converter is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2019-11-17 15:52:28 +01:00
|
|
|
* along with GDSII-Converter. If not, see <http://www.gnu.org/licenses/>.
|
2018-12-15 00:05:34 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @file external-renderer.c
|
|
|
|
* @brief This file implements the dynamic library loading for the external rendering feature
|
|
|
|
* @author Mario Hüttel <mario.huettel@gmx.net>
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2019-06-18 19:42:18 +02:00
|
|
|
* @addtogroup ExternalRenderer
|
2018-12-15 00:05:34 +01:00
|
|
|
* @{
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <dlfcn.h>
|
|
|
|
#include <stdio.h>
|
2019-11-12 21:15:36 +01:00
|
|
|
#include <sys/wait.h>
|
2019-12-12 21:22:14 +01:00
|
|
|
#include <glib/gi18n.h>
|
2018-12-15 00:05:34 +01:00
|
|
|
|
2019-06-17 21:50:49 +02:00
|
|
|
#include <gds-render/output-renderers/external-renderer.h>
|
2019-11-12 21:15:36 +01:00
|
|
|
#include <gds-render/version.h>
|
|
|
|
|
|
|
|
#define FORCE_FORK 0U /**< @brief if != 0, then forking is forced regardless of the shared object's settings */
|
2019-03-26 19:57:19 +01:00
|
|
|
|
2019-06-18 19:42:18 +02:00
|
|
|
struct _ExternalRenderer {
|
|
|
|
GdsOutputRenderer parent;
|
2019-06-18 21:08:29 +02:00
|
|
|
char *shared_object_path;
|
2019-11-15 21:04:59 +01:00
|
|
|
char *cli_param_string;
|
2019-06-18 21:08:29 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
PROP_SO_PATH = 1, /**< @brief Shared object path property */
|
2019-11-15 21:04:59 +01:00
|
|
|
PROP_PARAM_STRING, /** @brief Shared object renderer parameter string from CLI */
|
2019-06-18 21:08:29 +02:00
|
|
|
N_PROPERTIES /**< @brief Used to get property count */
|
2019-06-18 19:42:18 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(ExternalRenderer, external_renderer, GDS_RENDER_TYPE_OUTPUT_RENDERER)
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Execute render function in shared object to render the supplied cell
|
|
|
|
* @param toplevel_cell Cell to render
|
|
|
|
* @param layer_info_list Layer information (Color etc.)
|
|
|
|
* @param output_file Destination file
|
2019-08-24 13:45:36 +02:00
|
|
|
* @param scale the scaling value to scale the output cell down by.
|
2019-06-18 19:42:18 +02:00
|
|
|
* @param so_path Path to shared object
|
2019-11-15 21:04:59 +01:00
|
|
|
* @param params Parameters passed to EXTERNAL_LIBRARY_INIT_FUNCTION
|
2019-06-18 19:42:18 +02:00
|
|
|
* @return 0 if successful
|
|
|
|
*/
|
|
|
|
static int external_renderer_render_cell(struct gds_cell *toplevel_cell, GList *layer_info_list,
|
2019-11-15 21:04:59 +01:00
|
|
|
const char *output_file, double scale, const char *so_path, const char *params)
|
2018-12-15 00:05:34 +01:00
|
|
|
{
|
2019-06-18 19:42:18 +02:00
|
|
|
int (*so_render_func)(struct gds_cell *, GList *, const char *, double) = NULL;
|
2019-11-12 21:15:36 +01:00
|
|
|
int (*so_init_func)(const char *, const char *) = NULL;
|
2019-11-16 22:44:41 +01:00
|
|
|
int (*so_finalize_func)(void) = NULL;
|
2018-12-15 00:05:34 +01:00
|
|
|
void *so_handle = NULL;
|
|
|
|
char *error_msg;
|
2019-11-12 21:15:36 +01:00
|
|
|
int forking_req;
|
2018-12-15 00:05:34 +01:00
|
|
|
int ret = 0;
|
2019-11-12 21:15:36 +01:00
|
|
|
pid_t fork_pid = 0;
|
|
|
|
int forked_status;
|
2018-12-15 00:05:34 +01:00
|
|
|
|
2019-06-18 21:08:29 +02:00
|
|
|
if (!so_path) {
|
2019-12-12 21:22:14 +01:00
|
|
|
fprintf(stderr, _("Path to shared object not set!\n"));
|
2019-06-18 21:08:29 +02:00
|
|
|
return -3000;
|
|
|
|
}
|
|
|
|
|
2018-12-15 00:05:34 +01:00
|
|
|
/* Check parameter sanity */
|
2019-06-18 21:08:29 +02:00
|
|
|
if (!output_file || !toplevel_cell || !layer_info_list)
|
2018-12-15 00:05:34 +01:00
|
|
|
return -3000;
|
|
|
|
|
|
|
|
/* Load shared object */
|
|
|
|
so_handle = dlopen(so_path, RTLD_LAZY);
|
|
|
|
if (!so_handle) {
|
2019-12-12 21:22:14 +01:00
|
|
|
fprintf(stderr, _("Could not load external library '%s'\nDetailed error is:\n%s\n"), so_path, dlerror());
|
2018-12-15 00:05:34 +01:00
|
|
|
return -2000;
|
|
|
|
}
|
|
|
|
|
2019-11-12 21:15:36 +01:00
|
|
|
/* Load rendering symbol from library */
|
2019-10-25 21:16:31 +02:00
|
|
|
so_render_func = (int (*)(struct gds_cell *, GList *, const char *, double))
|
2019-11-12 20:52:42 +01:00
|
|
|
dlsym(so_handle, xstr(EXTERNAL_LIBRARY_RENDER_FUNCTION));
|
2019-03-07 20:35:23 +01:00
|
|
|
error_msg = dlerror();
|
2019-11-16 22:44:41 +01:00
|
|
|
if (error_msg) {
|
2019-12-12 21:22:14 +01:00
|
|
|
fprintf(stderr, _("Rendering function not found in library:\n%s\n"), error_msg);
|
2018-12-15 00:05:34 +01:00
|
|
|
goto ret_close_so_handle;
|
|
|
|
}
|
|
|
|
|
2019-11-12 21:15:36 +01:00
|
|
|
/* Load the init function */
|
|
|
|
so_init_func = (int (*)(const char *, const char *))dlsym(so_handle, xstr(EXTERNAL_LIBRARY_INIT_FUNCTION));
|
|
|
|
error_msg = dlerror();
|
2019-11-16 22:44:41 +01:00
|
|
|
if (error_msg) {
|
2020-02-10 23:06:40 +01:00
|
|
|
fprintf(stderr, _("Rendering function not found in library:\n%s\n"), error_msg);
|
2019-11-12 21:15:36 +01:00
|
|
|
goto ret_close_so_handle;
|
|
|
|
}
|
|
|
|
|
2019-11-16 22:44:41 +01:00
|
|
|
/* Load the finalize function */
|
|
|
|
so_finalize_func = (int (*)(void))dlsym(so_handle, xstr(EXTERNAL_LIBRARY_FINALIZE_FUNCTION));
|
|
|
|
error_msg = dlerror();
|
|
|
|
if (error_msg) {
|
|
|
|
fprintf(stderr, "Finalize function not found in library:\n%s\n", error_msg);
|
|
|
|
goto ret_close_so_handle;
|
|
|
|
}
|
|
|
|
|
2019-11-12 21:15:36 +01:00
|
|
|
/* Check if forking is requested */
|
|
|
|
if (dlsym(so_handle, xstr(EXTERNAL_LIBRARY_FORK_REQUEST)))
|
|
|
|
forking_req = 1;
|
|
|
|
else if (FORCE_FORK)
|
|
|
|
forking_req = 1;
|
|
|
|
else
|
|
|
|
forking_req = 0;
|
|
|
|
|
2018-12-15 00:05:34 +01:00
|
|
|
/* Execute */
|
2019-11-12 21:15:36 +01:00
|
|
|
|
2019-12-12 21:22:14 +01:00
|
|
|
g_message(_("Calling external renderer."));
|
2019-11-12 21:15:36 +01:00
|
|
|
|
|
|
|
if (forking_req)
|
|
|
|
fork_pid = fork();
|
|
|
|
if (fork_pid != 0)
|
|
|
|
goto end_forked;
|
|
|
|
|
2019-11-15 21:04:59 +01:00
|
|
|
ret = so_init_func(params, _app_version_string);
|
2019-11-12 21:15:36 +01:00
|
|
|
if (!ret)
|
2019-06-18 19:42:18 +02:00
|
|
|
ret = so_render_func(toplevel_cell, layer_info_list, output_file, scale);
|
2019-11-12 21:15:36 +01:00
|
|
|
|
2019-11-16 22:44:41 +01:00
|
|
|
/* Finalize the external renderer */
|
|
|
|
ret |= so_finalize_func();
|
|
|
|
|
2019-11-15 21:06:41 +01:00
|
|
|
/* If we are in a separate process, terminate here */
|
2019-11-12 21:15:36 +01:00
|
|
|
if (forking_req)
|
|
|
|
exit(ret);
|
|
|
|
|
2019-11-15 21:06:41 +01:00
|
|
|
/* The forked paths end here */
|
2019-11-12 21:15:36 +01:00
|
|
|
end_forked:
|
|
|
|
if (forking_req) {
|
|
|
|
waitpid(fork_pid, &forked_status, 0);
|
|
|
|
ret = WEXITSTATUS(forked_status);
|
2019-06-19 20:47:19 +02:00
|
|
|
}
|
2018-12-15 00:05:34 +01:00
|
|
|
|
2019-12-12 21:22:14 +01:00
|
|
|
g_message(_("External renderer finished."));
|
2019-11-12 21:15:36 +01:00
|
|
|
|
2018-12-15 00:05:34 +01:00
|
|
|
ret_close_so_handle:
|
|
|
|
dlclose(so_handle);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-06-18 19:42:18 +02:00
|
|
|
static int external_renderer_render_output(GdsOutputRenderer *renderer,
|
|
|
|
struct gds_cell *cell,
|
|
|
|
double scale)
|
|
|
|
{
|
|
|
|
ExternalRenderer *ext_renderer = GDS_RENDER_EXTERNAL_RENDERER(renderer);
|
2019-06-21 21:41:31 +02:00
|
|
|
LayerSettings *settings;
|
|
|
|
GList *layer_infos = NULL;
|
|
|
|
const char *output_file;
|
2019-08-19 19:54:53 +02:00
|
|
|
int ret;
|
2019-06-21 21:41:31 +02:00
|
|
|
|
|
|
|
output_file = gds_output_renderer_get_output_file(renderer);
|
2019-08-19 19:54:53 +02:00
|
|
|
settings = gds_output_renderer_get_and_ref_layer_settings(renderer);
|
2019-06-21 21:41:31 +02:00
|
|
|
|
|
|
|
/* Set layer info list. In case of failure it remains NULL */
|
|
|
|
if (settings)
|
|
|
|
layer_infos = layer_settings_get_layer_info_list(settings);
|
2019-06-18 19:42:18 +02:00
|
|
|
|
2019-11-15 21:04:59 +01:00
|
|
|
ret = external_renderer_render_cell(cell, layer_infos, output_file, scale, ext_renderer->shared_object_path,
|
|
|
|
ext_renderer->cli_param_string);
|
2019-08-19 19:54:53 +02:00
|
|
|
if (settings)
|
|
|
|
g_object_unref(settings);
|
|
|
|
|
|
|
|
return ret;
|
2019-06-18 19:42:18 +02:00
|
|
|
}
|
|
|
|
|
2019-06-18 21:08:29 +02:00
|
|
|
static void external_renderer_get_property(GObject *obj, guint property_id, GValue *value, GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ExternalRenderer *self;
|
|
|
|
|
|
|
|
self = GDS_RENDER_EXTERNAL_RENDERER(obj);
|
|
|
|
|
|
|
|
switch (property_id) {
|
|
|
|
case PROP_SO_PATH:
|
|
|
|
g_value_set_string(value, self->shared_object_path);
|
|
|
|
break;
|
2019-11-15 21:04:59 +01:00
|
|
|
case PROP_PARAM_STRING:
|
|
|
|
g_value_set_string(value, self->cli_param_string);
|
|
|
|
break;
|
2019-06-18 21:08:29 +02:00
|
|
|
default:
|
2019-10-25 21:16:31 +02:00
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
|
2019-06-18 21:08:29 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void external_renderer_set_property(GObject *obj, guint property_id, const GValue *value, GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ExternalRenderer *self;
|
|
|
|
|
|
|
|
self = GDS_RENDER_EXTERNAL_RENDERER(obj);
|
|
|
|
|
|
|
|
switch (property_id) {
|
|
|
|
case PROP_SO_PATH:
|
|
|
|
if (self->shared_object_path)
|
|
|
|
g_free(self->shared_object_path);
|
|
|
|
self->shared_object_path = g_value_dup_string(value);
|
|
|
|
break;
|
2019-11-15 21:04:59 +01:00
|
|
|
case PROP_PARAM_STRING:
|
|
|
|
if (self->cli_param_string)
|
|
|
|
g_free(self->cli_param_string);
|
|
|
|
self->cli_param_string = g_value_dup_string(value);
|
|
|
|
break;
|
2019-06-18 21:08:29 +02:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-19 20:47:19 +02:00
|
|
|
static void external_renderer_dispose(GObject *self_obj)
|
2019-06-18 21:46:02 +02:00
|
|
|
{
|
2019-11-15 21:04:59 +01:00
|
|
|
ExternalRenderer *self;
|
|
|
|
|
|
|
|
self = GDS_RENDER_EXTERNAL_RENDERER(self_obj);
|
2019-06-19 20:47:19 +02:00
|
|
|
|
2019-06-18 21:46:02 +02:00
|
|
|
if (self->shared_object_path) {
|
|
|
|
g_free(self->shared_object_path);
|
|
|
|
self->shared_object_path = NULL;
|
|
|
|
}
|
|
|
|
|
2019-06-21 21:41:31 +02:00
|
|
|
G_OBJECT_CLASS(external_renderer_parent_class)->dispose(self_obj);
|
2019-06-18 21:46:02 +02:00
|
|
|
}
|
|
|
|
|
2019-06-18 21:08:29 +02:00
|
|
|
static GParamSpec *external_renderer_properties[N_PROPERTIES] = {NULL};
|
|
|
|
|
2019-06-18 19:42:18 +02:00
|
|
|
static void external_renderer_class_init(ExternalRendererClass *klass)
|
|
|
|
{
|
|
|
|
GdsOutputRendererClass *inherited_parent_class;
|
2019-06-18 21:08:29 +02:00
|
|
|
GObjectClass *oclass;
|
2019-06-18 19:42:18 +02:00
|
|
|
|
|
|
|
inherited_parent_class = GDS_RENDER_OUTPUT_RENDERER_CLASS(klass);
|
2019-06-19 20:47:19 +02:00
|
|
|
oclass = G_OBJECT_CLASS(klass);
|
2019-06-18 19:42:18 +02:00
|
|
|
|
|
|
|
/* Override virtual function */
|
|
|
|
inherited_parent_class->render_output = external_renderer_render_output;
|
2019-06-18 21:08:29 +02:00
|
|
|
|
2019-06-18 21:46:02 +02:00
|
|
|
/* Setup Gobject callbacks */
|
2019-06-18 21:08:29 +02:00
|
|
|
oclass->set_property = external_renderer_set_property;
|
|
|
|
oclass->get_property = external_renderer_get_property;
|
2019-06-18 21:46:02 +02:00
|
|
|
oclass->dispose = external_renderer_dispose;
|
2019-06-18 21:08:29 +02:00
|
|
|
|
|
|
|
/* Setup properties */
|
|
|
|
external_renderer_properties[PROP_SO_PATH] =
|
2019-12-12 21:22:14 +01:00
|
|
|
g_param_spec_string(N_("shared-object-path"),
|
|
|
|
N_("Shared object file path"),
|
|
|
|
N_("Path to the shared object to search rendering function in."),
|
2019-06-18 21:08:29 +02:00
|
|
|
NULL,
|
|
|
|
G_PARAM_READWRITE);
|
2019-11-15 21:04:59 +01:00
|
|
|
external_renderer_properties[PROP_PARAM_STRING] =
|
2019-12-12 21:22:14 +01:00
|
|
|
g_param_spec_string(N_("param-string"),
|
|
|
|
N_("Shared object renderer parameter string"),
|
|
|
|
N_("Command line arguments passed to the external shared object renderer"),
|
2019-11-15 21:04:59 +01:00
|
|
|
NULL,
|
|
|
|
G_PARAM_READWRITE);
|
2019-06-18 21:08:29 +02:00
|
|
|
g_object_class_install_properties(oclass, N_PROPERTIES, external_renderer_properties);
|
2019-06-18 19:42:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void external_renderer_init(ExternalRenderer *self)
|
|
|
|
{
|
|
|
|
self->shared_object_path = NULL;
|
2019-11-15 21:04:59 +01:00
|
|
|
self->cli_param_string = NULL;
|
2019-06-18 19:42:18 +02:00
|
|
|
}
|
|
|
|
|
2019-06-18 21:08:29 +02:00
|
|
|
ExternalRenderer *external_renderer_new()
|
|
|
|
{
|
|
|
|
return g_object_new(GDS_RENDER_TYPE_EXTERNAL_RENDERER, NULL);
|
|
|
|
}
|
2019-06-18 19:42:18 +02:00
|
|
|
|
2019-11-15 21:11:41 +01:00
|
|
|
ExternalRenderer *external_renderer_new_with_so_and_param(const char *so_path, const char *param_string)
|
2019-06-18 21:10:53 +02:00
|
|
|
{
|
2019-12-12 21:22:14 +01:00
|
|
|
return g_object_new(GDS_RENDER_TYPE_EXTERNAL_RENDERER, N_("shared-object-path"), so_path,
|
|
|
|
N_("param-string"), param_string, NULL);
|
2019-06-18 21:10:53 +02:00
|
|
|
}
|
|
|
|
|
2018-12-15 00:05:34 +01:00
|
|
|
/** @} */
|