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"""
from setuptools.extension import Extension, SharedLibrary
from setuptools.dist import Distribution, Feature, _get_unpatched
import distutils.core, setuptools.command
from setuptools.extension import Extension
from setuptools.depends import Require
from distutils.core import Command as _Command
from distutils.util import convert_path
......
......@@ -7,9 +7,11 @@ except ImportError:
import os, sys
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):
def run(self):
"""Build extensions in build directory, then copy if --inplace"""
old_inplace, self.inplace = self.inplace, 0
......@@ -21,15 +23,13 @@ class build_ext(_build_ext):
def copy_extensions_to_source(self):
build_py = self.get_finalized_command('build_py')
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('.')
package = '.'.join(modpath[:-1])
base = modpath[-1]
package_dir = build_py.get_package_dir(package)
dest_filename = os.path.join(package_dir,
self.get_ext_filename(base))
src_filename = os.path.join(self.build_lib,
self.get_ext_filename(fullname))
dest_filename = os.path.join(package_dir,os.path.basename(filename))
src_filename = os.path.join(self.build_lib,filename)
# Always copy, even if source is older than destination, to ensure
# that the right extensions for the current Python/platform are
......@@ -47,6 +47,88 @@ class build_ext(_build_ext):
# Then do any actual SWIG stuff on the remainder
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']
from distutils.core import Distribution as _Distribution
from distutils.core import Extension
from setuptools.depends import Require
from setuptools.command.build_ext import build_ext
from setuptools.command.install import install
from setuptools.command.sdist import sdist
from setuptools.command.install_lib import install_lib
......@@ -39,6 +37,8 @@ sequence = tuple, list
def assert_string_list(dist, attr, value):
"""Verify that value is a string list or None"""
try:
......
from distutils.core import Extension as _Extension
from dist import _get_unpatched
_Extension = _get_unpatched(_Extension)
try:
from Pyrex.Distutils.build_ext import build_ext
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):
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):
_Extension.__init__(self,*args,**kw)
sources = []
......@@ -24,14 +25,12 @@ except ImportError:
sources.append(s)
self.sources = sources
import sys, distutils.core, distutils.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
else:
class SharedLibrary(Extension):
"""Just like a regular Extension, but built as a shared library instead"""
# Pyrex is here, just use regular extension type
Extension = _Extension
import sys, distutils.core, distutils.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