Commit bda5b372 authored by PJ Eby's avatar PJ Eby

First draft of shared library build support. See tests/shlib_test

for a trivial example.  This has only been tested on Windows with
a MinGW compiler, and the Mac OS support isn't finished.  Testing
w/other platforms+compilers would be helpful.

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041927
parent b28da64e
"""Extensions to the 'distutils' for large or complex distributions""" """Extensions to the 'distutils' for large or complex distributions"""
from setuptools.extension import Extension, SharedLibrary
from setuptools.dist import Distribution, Feature, _get_unpatched from setuptools.dist import Distribution, Feature, _get_unpatched
import distutils.core, setuptools.command import distutils.core, setuptools.command
from setuptools.extension import Extension
from setuptools.depends import Require from setuptools.depends import Require
from distutils.core import Command as _Command from distutils.core import Command as _Command
from distutils.util import convert_path from distutils.util import convert_path
......
...@@ -7,9 +7,11 @@ except ImportError: ...@@ -7,9 +7,11 @@ except ImportError:
import os, sys import os, sys
from distutils.file_util import copy_file from distutils.file_util import copy_file
from setuptools.extension import SharedLibrary
from distutils.ccompiler import new_compiler
from distutils.sysconfig import customize_compiler
class build_ext(_build_ext): class build_ext(_build_ext):
def run(self): def run(self):
"""Build extensions in build directory, then copy if --inplace""" """Build extensions in build directory, then copy if --inplace"""
old_inplace, self.inplace = self.inplace, 0 old_inplace, self.inplace = self.inplace, 0
...@@ -21,15 +23,13 @@ class build_ext(_build_ext): ...@@ -21,15 +23,13 @@ class build_ext(_build_ext):
def copy_extensions_to_source(self): def copy_extensions_to_source(self):
build_py = self.get_finalized_command('build_py') build_py = self.get_finalized_command('build_py')
for ext in self.extensions or (): for ext in self.extensions or ():
fullname = ext.name fullname = self.get_ext_fullname(ext.name)
filename = self.get_ext_filename(fullname)
modpath = fullname.split('.') modpath = fullname.split('.')
package = '.'.join(modpath[:-1]) package = '.'.join(modpath[:-1])
base = modpath[-1]
package_dir = build_py.get_package_dir(package) package_dir = build_py.get_package_dir(package)
dest_filename = os.path.join(package_dir, dest_filename = os.path.join(package_dir,os.path.basename(filename))
self.get_ext_filename(base)) src_filename = os.path.join(self.build_lib,filename)
src_filename = os.path.join(self.build_lib,
self.get_ext_filename(fullname))
# Always copy, even if source is older than destination, to ensure # Always copy, even if source is older than destination, to ensure
# that the right extensions for the current Python/platform are # that the right extensions for the current Python/platform are
...@@ -47,6 +47,88 @@ class build_ext(_build_ext): ...@@ -47,6 +47,88 @@ class build_ext(_build_ext):
# Then do any actual SWIG stuff on the remainder # Then do any actual SWIG stuff on the remainder
return _du_build_ext.swig_sources(self, sources, *otherargs) return _du_build_ext.swig_sources(self, sources, *otherargs)
def get_ext_filename(self, fullname):
filename = _build_ext.get_ext_filename(self,fullname)
for ext in self.shlibs:
if self.get_ext_fullname(ext.name)==fullname:
fn, ext = os.path.splitext(filename)
fn = self.shlib_compiler.library_filename(fn,'shared')
print "shlib",fn
return fn
return filename
def initialize_options(self):
_build_ext.initialize_options(self)
self.shlib_compiler = None
self.shlibs = []
def finalize_options(self):
_build_ext.finalize_options(self)
self.shlibs = [ext for ext in self.extensions or ()
if isinstance(ext,SharedLibrary)]
if self.shlibs:
self.setup_shlib_compiler()
self.library_dirs.append(self.build_lib)
def build_extension(self, ext):
_compiler = self.compiler
try:
if isinstance(ext,SharedLibrary):
self.compiler = self.shlib_compiler
_build_ext.build_extension(self,ext)
finally:
self.compiler = _compiler
def setup_shlib_compiler(self):
compiler = self.shlib_compiler = new_compiler(
compiler=self.compiler, dry_run=self.dry_run, force=self.force
)
customize_compiler(compiler)
if sys.platform == "darwin":
# XXX need to fix up compiler_so:ccshared + linker_so:ldshared too
compiler.shared_lib_extension = ".dylib"
if self.include_dirs is not None:
compiler.set_include_dirs(self.include_dirs)
if self.define is not None:
# 'define' option is a list of (name,value) tuples
for (name,value) in self.define:
compiler.define_macro(name, value)
if self.undef is not None:
for macro in self.undef:
compiler.undefine_macro(macro)
if self.libraries is not None:
compiler.set_libraries(self.libraries)
if self.library_dirs is not None:
compiler.set_library_dirs(self.library_dirs)
if self.rpath is not None:
compiler.set_runtime_library_dirs(self.rpath)
if self.link_objects is not None:
compiler.set_link_objects(self.link_objects)
# hack so distutils' build_extension() builds a shared lib instead
#
def link_shared_object(self, objects, output_libname, output_dir=None,
libraries=None, library_dirs=None, runtime_library_dirs=None,
export_symbols=None, debug=0, extra_preargs=None,
extra_postargs=None, build_temp=None, target_lang=None
): self.link(
self.SHARED_LIBRARY, objects, output_libname,
output_dir, libraries, library_dirs, runtime_library_dirs,
export_symbols, debug, extra_preargs, extra_postargs,
build_temp, target_lang
)
compiler.link_shared_object = link_shared_object.__get__(compiler)
def get_export_symbols(self, ext):
if isinstance(ext,SharedLibrary):
return ext.export_symbols
return _build_ext.get_export_symbols(self,ext)
......
__all__ = ['Distribution', 'Feature'] __all__ = ['Distribution', 'Feature']
from distutils.core import Distribution as _Distribution from distutils.core import Distribution as _Distribution
from distutils.core import Extension
from setuptools.depends import Require from setuptools.depends import Require
from setuptools.command.build_ext import build_ext
from setuptools.command.install import install from setuptools.command.install import install
from setuptools.command.sdist import sdist from setuptools.command.sdist import sdist
from setuptools.command.install_lib import install_lib from setuptools.command.install_lib import install_lib
...@@ -39,6 +37,8 @@ sequence = tuple, list ...@@ -39,6 +37,8 @@ sequence = tuple, list
def assert_string_list(dist, attr, value): def assert_string_list(dist, attr, value):
"""Verify that value is a string list or None""" """Verify that value is a string list or None"""
try: try:
......
from distutils.core import Extension as _Extension from distutils.core import Extension as _Extension
from dist import _get_unpatched
_Extension = _get_unpatched(_Extension)
try: try:
from Pyrex.Distutils.build_ext import build_ext from Pyrex.Distutils.build_ext import build_ext
except ImportError: except ImportError:
have_pyrex = False
else:
have_pyrex = True
# Pyrex isn't around, so fix up the sources
from dist import _get_unpatched
_Extension = _get_unpatched(_Extension)
class Extension(_Extension):
"""Extension that uses '.c' files in place of '.pyx' files""" class Extension(_Extension):
"""Extension that uses '.c' files in place of '.pyx' files"""
if not have_pyrex:
# convert .pyx extensions to .c
def __init__(self,*args,**kw): def __init__(self,*args,**kw):
_Extension.__init__(self,*args,**kw) _Extension.__init__(self,*args,**kw)
sources = [] sources = []
...@@ -24,14 +25,12 @@ except ImportError: ...@@ -24,14 +25,12 @@ except ImportError:
sources.append(s) sources.append(s)
self.sources = sources self.sources = sources
import sys, distutils.core, distutils.extension class SharedLibrary(Extension):
distutils.core.Extension = Extension """Just like a regular Extension, but built as a shared library instead"""
distutils.extension.Extension = Extension
if 'distutils.command.build_ext' in sys.modules:
sys.modules['distutils.command.build_ext'].Extension = Extension
else:
# Pyrex is here, just use regular extension type import sys, distutils.core, distutils.extension
Extension = _Extension distutils.core.Extension = Extension
distutils.extension.Extension = Extension
if 'distutils.command.build_ext' in sys.modules:
sys.modules['distutils.command.build_ext'].Extension = Extension
/* Generated by Pyrex 0.9.3 on Thu Jan 05 17:47:12 2006 */
#include "Python.h"
#include "structmember.h"
#ifndef PY_LONG_LONG
#define PY_LONG_LONG LONG_LONG
#endif
typedef struct {PyObject **p; char *s;} __Pyx_InternTabEntry; /*proto*/
typedef struct {PyObject **p; char *s; long n;} __Pyx_StringTabEntry; /*proto*/
static PyObject *__Pyx_UnpackItem(PyObject *, int); /*proto*/
static int __Pyx_EndUnpack(PyObject *, int); /*proto*/
static int __Pyx_PrintItem(PyObject *); /*proto*/
static int __Pyx_PrintNewline(void); /*proto*/
static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb); /*proto*/
static void __Pyx_ReRaise(void); /*proto*/
static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/
static PyObject *__Pyx_GetExcValue(void); /*proto*/
static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed, char *name); /*proto*/
static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/
static int __Pyx_GetStarArgs(PyObject **args, PyObject **kwds, char *kwd_list[], int nargs, PyObject **args2, PyObject **kwds2); /*proto*/
static void __Pyx_WriteUnraisable(char *name); /*proto*/
static void __Pyx_AddTraceback(char *funcname); /*proto*/
static PyTypeObject *__Pyx_ImportType(char *module_name, char *class_name, long size); /*proto*/
static int __Pyx_SetVtable(PyObject *dict, void *vtable); /*proto*/
static int __Pyx_GetVtable(PyObject *dict, void *vtabptr); /*proto*/
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, char *modname); /*proto*/
static int __Pyx_InternStrings(__Pyx_InternTabEntry *t); /*proto*/
static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/
static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/
static PyObject *__pyx_m;
static PyObject *__pyx_b;
static int __pyx_lineno;
static char *__pyx_filename;
staticforward char **__pyx_f;
/* Declarations from hello */
char (*(get_hello_msg(void))); /*proto*/
/* Implementation of hello */
static PyObject *__pyx_n_hello;
static PyObject *__pyx_f_5hello_hello(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static PyObject *__pyx_f_5hello_hello(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
PyObject *__pyx_r;
PyObject *__pyx_1 = 0;
static char *__pyx_argnames[] = {0};
if (!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames)) return 0;
/* "C:\cygwin\home\pje\setuptools\tests\shlib_test\hello.pyx":4 */
__pyx_1 = PyString_FromString(get_hello_msg()); if (!__pyx_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 4; goto __pyx_L1;}
__pyx_r = __pyx_1;
__pyx_1 = 0;
goto __pyx_L0;
__pyx_r = Py_None; Py_INCREF(__pyx_r);
goto __pyx_L0;
__pyx_L1:;
Py_XDECREF(__pyx_1);
__Pyx_AddTraceback("hello.hello");
__pyx_r = 0;
__pyx_L0:;
return __pyx_r;
}
static __Pyx_InternTabEntry __pyx_intern_tab[] = {
{&__pyx_n_hello, "hello"},
{0, 0}
};
static struct PyMethodDef __pyx_methods[] = {
{"hello", (PyCFunction)__pyx_f_5hello_hello, METH_VARARGS|METH_KEYWORDS, 0},
{0, 0, 0, 0}
};
DL_EXPORT(void) inithello(void); /*proto*/
DL_EXPORT(void) inithello(void) {
__pyx_m = Py_InitModule4("hello", __pyx_methods, 0, 0, PYTHON_API_VERSION);
if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
__pyx_b = PyImport_AddModule("__builtin__");
if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
if (__Pyx_InternStrings(__pyx_intern_tab) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
/* "C:\cygwin\home\pje\setuptools\tests\shlib_test\hello.pyx":3 */
return;
__pyx_L1:;
__Pyx_AddTraceback("hello");
}
static char *__pyx_filenames[] = {
"hello.pyx",
};
statichere char **__pyx_f = __pyx_filenames;
/* Runtime support code */
static int __Pyx_InternStrings(__Pyx_InternTabEntry *t) {
while (t->p) {
*t->p = PyString_InternFromString(t->s);
if (!*t->p)
return -1;
++t;
}
return 0;
}
#include "compile.h"
#include "frameobject.h"
#include "traceback.h"
static void __Pyx_AddTraceback(char *funcname) {
PyObject *py_srcfile = 0;
PyObject *py_funcname = 0;
PyObject *py_globals = 0;
PyObject *empty_tuple = 0;
PyObject *empty_string = 0;
PyCodeObject *py_code = 0;
PyFrameObject *py_frame = 0;
py_srcfile = PyString_FromString(__pyx_filename);
if (!py_srcfile) goto bad;
py_funcname = PyString_FromString(funcname);
if (!py_funcname) goto bad;
py_globals = PyModule_GetDict(__pyx_m);
if (!py_globals) goto bad;
empty_tuple = PyTuple_New(0);
if (!empty_tuple) goto bad;
empty_string = PyString_FromString("");
if (!empty_string) goto bad;
py_code = PyCode_New(
0, /*int argcount,*/
0, /*int nlocals,*/
0, /*int stacksize,*/
0, /*int flags,*/
empty_string, /*PyObject *code,*/
empty_tuple, /*PyObject *consts,*/
empty_tuple, /*PyObject *names,*/
empty_tuple, /*PyObject *varnames,*/
empty_tuple, /*PyObject *freevars,*/
empty_tuple, /*PyObject *cellvars,*/
py_srcfile, /*PyObject *filename,*/
py_funcname, /*PyObject *name,*/
__pyx_lineno, /*int firstlineno,*/
empty_string /*PyObject *lnotab*/
);
if (!py_code) goto bad;
py_frame = PyFrame_New(
PyThreadState_Get(), /*PyThreadState *tstate,*/
py_code, /*PyCodeObject *code,*/
py_globals, /*PyObject *globals,*/
0 /*PyObject *locals*/
);
if (!py_frame) goto bad;
py_frame->f_lineno = __pyx_lineno;
PyTraceBack_Here(py_frame);
bad:
Py_XDECREF(py_srcfile);
Py_XDECREF(py_funcname);
Py_XDECREF(empty_tuple);
Py_XDECREF(empty_string);
Py_XDECREF(py_code);
Py_XDECREF(py_frame);
}
cdef extern char *get_hello_msg()
def hello():
return get_hello_msg()
extern char* get_hello_msg() {
return "Hello, world!";
}
from setuptools import setup, Extension, SharedLibrary
setup(
name="shlib_test",
ext_modules = [
SharedLibrary("hellolib", ["hellolib.c"]),
Extension("hello", ["hello.pyx"], libraries=["hellolib"])
],
test_suite="test_hello.HelloWorldTest",
)
from unittest import TestCase
class HelloWorldTest(TestCase):
def testHelloMsg(self):
from hello import hello
self.assertEqual(hello(), "Hello, world!")
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment