#include "pynetclr.h"
#include "stdlib.h"
#ifndef _WIN32
#include "dirent.h"
#include "dlfcn.h"
#include "libgen.h"
#include "alloca.h"
#endif
// initialize Mono and PythonNet
PyNet_Args *PyNet_Init(int ext)
{
PyNet_Args *pn_args;
pn_args = (PyNet_Args *)malloc(sizeof(PyNet_Args));
pn_args->pr_file = PR_ASSEMBLY;
pn_args->error = NULL;
pn_args->shutdown = NULL;
pn_args->module = NULL;
if (ext == 0)
{
pn_args->init_name = "Python.Runtime:Initialize()";
}
else
{
pn_args->init_name = "Python.Runtime:InitExt()";
}
pn_args->shutdown_name = "Python.Runtime:Shutdown()";
pn_args->domain = mono_jit_init_version(MONO_DOMAIN, MONO_VERSION);
mono_domain_set_config(pn_args->domain, ".", "Python.Runtime.dll.config");
/*
* Load the default Mono configuration file, this is needed
* if you are planning on using the dllmaps defined on the
* system configuration
*/
mono_config_parse(NULL);
/* I can't use this call to run the main_thread_handler. The function
* runs it in another thread but *this* thread holds the Python
* import lock -> DEAD LOCK.
*
* mono_runtime_exec_managed_code(pn_args->domain, main_thread_handler,
* pn_args);
*/
main_thread_handler(pn_args);
if (pn_args->error != NULL)
{
PyErr_SetString(PyExc_ImportError, pn_args->error);
}
return pn_args;
}
// Shuts down PythonNet and cleans up Mono
void PyNet_Finalize(PyNet_Args *pn_args)
{
MonoObject *exception = NULL;
if (pn_args->shutdown)
{
mono_runtime_invoke(pn_args->shutdown, NULL, NULL, &exception);
if (exception)
{
pn_args->error = PyNet_ExceptionToString(exception);
}
pn_args->shutdown = NULL;
}
if (pn_args->domain)
{
mono_jit_cleanup(pn_args->domain);
pn_args->domain = NULL;
}
free(pn_args);
}
MonoMethod *getMethodFromClass(MonoClass *cls, char *name)
{
MonoMethodDesc *mdesc;
MonoMethod *method;
mdesc = mono_method_desc_new(name, 1);
method = mono_method_desc_search_in_class(mdesc, cls);
mono_method_desc_free(mdesc);
return method;
}
void main_thread_handler(gpointer user_data)
{
PyNet_Args *pn_args = (PyNet_Args *)user_data;
MonoMethod *init;
MonoImage *pr_image;
MonoClass *pythonengine;
MonoObject *exception = NULL;
MonoObject *init_result;
#ifndef _WIN32
// Get the filename of the python shared object and set
// LD_LIBRARY_PATH so Mono can find it.
Dl_info dlinfo = {0};
if (0 != dladdr(&Py_Initialize, &dlinfo))
{
char *fname = alloca(strlen(dlinfo.dli_fname) + 1);
strcpy(fname, dlinfo.dli_fname);
char *py_libdir = dirname(fname);
char *ld_library_path = getenv("LD_LIBRARY_PATH");
if (NULL == ld_library_path)
{
setenv("LD_LIBRARY_PATH", py_libdir, 1);
}
else
{
char *new_ld_library_path = alloca(strlen(py_libdir)
+ strlen(ld_library_path)
+ 2);
strcpy(new_ld_library_path, py_libdir);
strcat(new_ld_library_path, ":");
strcat(new_ld_library_path, ld_library_path);
setenv("LD_LIBRARY_PATH", py_libdir, 1);
}
}
//get python path system variable
PyObject *syspath = PySys_GetObject("path");
char *runtime_full_path = (char*) malloc(1024);
const char *slash = "/";
int found = 0;
int ii = 0;
for (ii = 0; ii < PyList_Size(syspath); ++ii)
{
#if PY_MAJOR_VERSION >= 3
Py_ssize_t wlen;
wchar_t *wstr = PyUnicode_AsWideCharString(PyList_GetItem(syspath, ii), &wlen);
char *pydir = (char*)malloc(wlen + 1);
size_t mblen = wcstombs(pydir, wstr, wlen + 1);
if (mblen > wlen)
pydir[wlen] = '\0';
PyMem_Free(wstr);
#else
const char *pydir = PyString_AsString(PyList_GetItem(syspath, ii));
#endif
char *curdir = (char*) malloc(1024);
strncpy(curdir, strlen(pydir) > 0 ? pydir : ".", 1024);
strncat(curdir, slash, 1024);
#if PY_MAJOR_VERSION >= 3
free(pydir);
#endif
//look in this directory for the pn_args->pr_file
DIR *dirp = opendir(curdir);
if (dirp != NULL)
{
struct dirent *dp;
while ((dp = readdir(dirp)) != NULL)
{
if (strcmp(dp->d_name, pn_args->pr_file) == 0)
{
strcpy(runtime_full_path, curdir);
strcat(runtime_full_path, pn_args->pr_file);
found = 1;
break;
}
}
closedir(dirp);
}
free(curdir);
if (found)
{
pn_args->pr_file = runtime_full_path;
break;
}
}
if (!found)
{
fprintf(stderr, "Could not find assembly %s. \n", pn_args->pr_file);
return;
}
#endif
pn_args->pr_assm = mono_domain_assembly_open(pn_args->domain, pn_args->pr_file);
if (!pn_args->pr_assm)
{
pn_args->error = "Unable to load assembly";
return;
}
#ifndef _WIN32
free(runtime_full_path);
#endif
pr_image = mono_assembly_get_image(pn_args->pr_assm);
if (!pr_image)
{
pn_args->error = "Unable to get image";
return;
}
pythonengine = mono_class_from_name(pr_image, "Python.Runtime", "PythonEngine");
if (!pythonengine)
{
pn_args->error = "Unable to load class PythonEngine from Python.Runtime";
return;
}
init = getMethodFromClass(pythonengine, pn_args->init_name);
if (!init)
{
pn_args->error = "Unable to fetch Init method from PythonEngine";
return;
}
pn_args->shutdown = getMethodFromClass(pythonengine, pn_args->shutdown_name);
if (!pn_args->shutdown)
{
pn_args->error = "Unable to fetch shutdown method from PythonEngine";
return;
}
init_result = mono_runtime_invoke(init, NULL, NULL, &exception);
if (exception)
{
pn_args->error = PyNet_ExceptionToString(exception);
return;
}
#if PY_MAJOR_VERSION >= 3
if (NULL != init_result)
pn_args->module = *(PyObject**)mono_object_unbox(init_result);
#endif
}
// Get string from a Mono exception
char *PyNet_ExceptionToString(MonoObject *e)
{
MonoMethodDesc *mdesc = mono_method_desc_new(":ToString()", FALSE);
MonoMethod *mmethod = mono_method_desc_search_in_class(mdesc, mono_get_object_class());
mono_method_desc_free(mdesc);
mmethod = mono_object_get_virtual_method(e, mmethod);
MonoString *monoString = (MonoString*) mono_runtime_invoke(mmethod, e, NULL, NULL);
mono_runtime_invoke(mmethod, e, NULL, NULL);
return mono_string_to_utf8(monoString);
}