[Zope-Perl] features.

Gisle Aas gisle@ActiveState.com
01 Feb 2001 16:46:52 -0800


Gisle Aas <gisle@ActiveState.com> writes:

> > 7.                                   Relatedly, do we know of a stock
> >     Python/Perl combination that does not require patching of Python?
> >     I'm using Zope-Perl happily with AS Perl 623 and stock Python 1.5.2
> >     on Windows.  Are there particular configurations which require
> >     patching?  If we adopt Python 2.X, will we still potentially
> >     require that folks build their own patched Python?  It's not
> >     really clear from the docs under what configurations you need
> >     to patch.
> 
> The patching is needed on platforms that use dlopen() to load dynamic
> libraries.  This include Linux and Solaris.  I don't know what the
> requirements are for other Unix platforms.  For windows no patching
> should be needed.
> 
> If the Python dist provide the "dl" module then it should actually be
> possible to avoid patching even on Linux and Solaris.  The RTLD_GLOBAL
> can actually be turned on after the library has been loaded by code
> like this:
> 
>     import perl  # python will load it with dlopen() the standard way
> 
>     # turn on the RTLD_GLOBAL flag for the perl module so that perl
>     # can load perl XS-extensions.
>     try:
>        import dl
>        dl.open(perl.__file__, dl.RTLD_NOW | dl.RTLD_GLOBAL)
>     except:
>        pass
> 
>     # we should now be able to use perl extensions
>     perl.eval("use Socket");
> 
> One problem is that this technique appears to require glibc-2.2 to
> work and current Linux distributions have not really moved there yet.

This is a better hack.  After this patch to pyperl there should not be
any need for patching neither perl nor python on Linux or Solaris.  I
can now build pyperl on top of the standard Active{Perl,Python}
distributions.

After the patch 'import perl' will actually just load a fake module
("dlhack.c") whose only purpose is to track down the real module and
dlopen(..., RTLD_GLOBAL) it.  After that everything works as it
should.  The idea for this cool workaround popped out of Sarathy's
head last week.

Regards,
Gisle


# To apply this patch, chdir to you source directory and enter
#
#     /bin/sh <this-file>
#     patch -p1 -N < <this-file>

touch dlhack.c
exit

Index: MANIFEST
####### pyperl/ => pyperl
--- pyperl.old/MANIFEST	Thu Feb  1 16:29:28 2001
+++ pyperl/MANIFEST	Thu Feb  1 16:29:55 2001
@@ -5,6 +5,7 @@
 TODO
 dbi-test.py
 dbi.py
+dlhack.c
 lang_lock.c
 lang_lock.h
 lang_map.c
Index: dlhack.c
####### pyperl/ => pyperl
--- /dev/null	Tue May  5 13:32:27 1998
+++ pyperl/dlhack.c	Thu Feb  1 16:29:55 2001
@@ -0,0 +1,50 @@
+#include <Python.h>
+#include <dlfcn.h>
+
+/* This is a fake perl module that will look for the real thing ('perl2.so')
+ * in sys.path and then load this one with the RTLD_GLOBAL set in order to
+ * make the symbols available for extension modules that perl might load.
+ */
+
+extern void initperl()
+{
+    void* handle;
+    int i, npath, len;
+    char buf[1024];
+
+    PyObject *path = PySys_GetObject("path");
+    if (path == NULL || !PyList_Check(path)) {
+	PyErr_SetString(PyExc_ImportError,
+			"sys.path must be a list of directory names");
+	return;
+    }
+
+    npath = PyList_Size(path);
+    for (i = 0; i < npath; i++) {
+	PyObject *v = PyList_GetItem(path, i);
+	if (!PyString_Check(v))
+	    continue;
+	len = PyString_Size(v);
+	if (len + 10 >= sizeof(buf))
+	    continue; /* Too long */
+	strcpy(buf, PyString_AsString(v));
+	if (buf[0] != '/')
+	    continue; /* Not absolute */
+	if (strlen(buf) != len)
+	    continue; /* v contains '\0' */
+	strcpy(buf+len, "/perl2.so");
+
+	handle = dlopen(buf, RTLD_NOW | RTLD_GLOBAL);
+	if (handle) {
+	    void (*f)() = dlsym(handle, "initperl2");
+	    if (f) {
+		f();
+	    }
+	    else {
+		PyErr_SetString(PyExc_ImportError, "initperl2 entry point not found");
+	    }
+	    return;
+	}
+    }
+    PyErr_SetString(PyExc_ImportError, "perl2.so not found");
+}
Index: patches/README
####### pyperl/ => pyperl
--- pyperl.old/patches/README	Thu Feb  1 16:29:28 2001
+++ pyperl/patches/README	Thu Feb  1 16:29:55 2001
@@ -15,6 +15,8 @@
     inclusion in upcoming perl-5.6.1 (change #6125).
 
 python-20-dynload-global
+    THIS PATCH SHOULD NOT BE NEEDED ANY MORE!!
+
     This patch to python-2.0 is needed to be able to load
     XS modules with the perl embedded in python.  It make sure
     dynamic objects are loaded with the RTLD_GLOBAL flag, so
Index: perlmodule.c
####### pyperl/ => pyperl
--- pyperl.old/perlmodule.c	Thu Feb  1 16:29:28 2001
+++ pyperl/perlmodule.c	Thu Feb  1 16:29:55 2001
@@ -730,29 +730,6 @@
     return pyo;
 }
 
-#if 0
-#include <dlfcn.h>
-
-static PyObject *
-dl_make_global(self, args, keywds)
-     PyObject *self;
-     PyObject *args;
-     PyObject *keywds;
-{
-    char *file;
-    void *handle;
-
-    if (!PyArg_ParseTuple(args, "s:perl.dl_global", &file))
-	return NULL;
-    handle = dlopen(file, RTLD_NOW | RTLD_GLOBAL);
-    if (!handle)
-	fprintf(stderr, "dlopen: %s\n", dlerror());
-    if (handle)
-	dlclose(handle);
-    return Py_BuildValue("");
-}
-#endif
-
 static PyMethodDef PerlMethods[] = {
     { "call",        call,        METH_VARARGS|METH_KEYWORDS},
     { "call_tuple",  call_tuple,  METH_VARARGS|METH_KEYWORDS},
@@ -766,15 +743,16 @@
     { "defined",     defined,     METH_VARARGS},
     { "get_ref",     get_ref,     METH_VARARGS|METH_KEYWORDS},
     { "array",       array,       METH_VARARGS},
-#ifdef RTLD_GLOBAL
-    { "dl_make_global", dl_make_global, METH_VARARGS},
-#endif
     { NULL, NULL } /* Sentinel */
 };
 
 
 void
+#ifdef DL_HACK
+initperl2()
+#else
 initperl()
+#endif
 {
     PyObject *m, *d;
 #ifndef MULTI_PERL
Index: setup.py
####### pyperl/ => pyperl
--- pyperl.old/setup.py	Thu Feb  1 16:29:28 2001
+++ pyperl/setup.py	Thu Feb  1 16:29:55 2001
@@ -1,5 +1,7 @@
 #!/usr/bin/env python
 
+from distutils.core import setup, Extension
+
 DEBUG = 0
 perl = 'perl'
 
@@ -17,6 +19,7 @@
 perl_ldopts = p.readline()
 p.close()
 
+ext_name     = "perl"
 include_dirs = []
 macros       = []
 cc_extra     = []
@@ -42,6 +45,8 @@
 o_extra   = []
 sym_extra   = []
 
+extra_ext = []
+
 # XXX hack name to get it to compile as C++ file on Windows
 svrv_object_c_name = "svrv_object.c"
 if sys.platform[:3] == "win":
@@ -51,6 +56,7 @@
         os.chmod(svrv_object_c_name, 0777)
         os.unlink(svrv_object_c_name)
     shutil.copy("svrv_object.c", svrv_object_c_name)
+
 sources = ['perlmodule.c',
            'lang_lock.c',
            'lang_map.c',
@@ -84,6 +90,18 @@
         system(perl + " -MExtUtils::Embed -e xsinit")
     sources.append('perlxsi.c');
 
+    # Try to figure out if we use dlopen on this platform
+    p = popen(perl + ' -V:dlsrc')
+    dlsrc = p.readline()
+    p.close()
+    if dlsrc == "dlsrc='dl_dlopen.xs';\n":
+        ext_name = "perl2"
+        cc_extra.append("-DDL_HACK")
+        extra_ext.append(Extension(name = "perl",
+                                   sources = ["dlhack.c"],
+                                   libraries = ["dl"],
+                                   ))
+        
 
 if MULTI_PERL:
     cc_extra.append("-DMULTI_PERL")
@@ -103,8 +121,6 @@
     sym_extra.append('sv2pyo')
     sym_extra.append('pyo2sv')
 
-from distutils.core import setup, Extension
-
 if DEBUG:
     print "Macros:", macros
     print "Include: ", include_dirs
@@ -114,6 +130,21 @@
     print "Lib dirs:",  lib_dirs
     print "Extra LD: ", ld_extra
 
+ext_modules = []
+ext_modules.append(Extension(name = ext_name,
+                             sources = sources,
+                             define_macros = macros,
+                             include_dirs = include_dirs,
+                             extra_compile_args = cc_extra,
+                             
+                             extra_objects =  o_extra,
+                             libraries = libs,
+                             library_dirs = lib_dirs,
+                             extra_link_args = ld_extra,
+                             export_symbols = sym_extra,
+                             ))
+ext_modules.extend(extra_ext)
+
 setup (name        = "pyperl",
        version     = "1.0.beta6",
        description = "Embed a perl interpreter",
@@ -121,17 +152,5 @@
        author      = "ActiveState Tool Corp.",
        author_email= "gisle@ActiveState.com",
        py_modules  = ['dbi', 'perlpickle'],
-       ext_modules = [Extension(name = "perl",
-	                        sources = sources,
-                                define_macros = macros,
-                                include_dirs = include_dirs,
-			        extra_compile_args = cc_extra,
-
-			        extra_objects =  o_extra,
-			        libraries = libs,
-                                library_dirs = lib_dirs,
-                                extra_link_args = ld_extra,
-                                export_symbols = sym_extra,
-                               ),
-		     ],
+       ext_modules = ext_modules,
       )