1from distutils.command.build_ext import build_ext as _du_build_ext
2try:
3    # Attempt to use Pyrex for building extensions, if available
4    from Pyrex.Distutils.build_ext import build_ext as _build_ext
5except ImportError:
6    _build_ext = _du_build_ext
7
8import os, sys
9from distutils.file_util import copy_file
10
11from distutils.tests.setuptools_extension import Library
12
13from distutils.ccompiler import new_compiler
14from distutils.sysconfig import customize_compiler, get_config_var
15get_config_var("LDSHARED")  # make sure _config_vars is initialized
16from distutils.sysconfig import _config_vars
17from distutils import log
18from distutils.errors import *
19
20have_rtld = False
21use_stubs = False
22libtype = 'shared'
23
24if sys.platform == "darwin":
25    use_stubs = True
26elif os.name != 'nt':
27    try:
28        from dl import RTLD_NOW
29        have_rtld = True
30        use_stubs = True
31    except ImportError:
32        pass
33
34def if_dl(s):
35    if have_rtld:
36        return s
37    return ''
38
39
40
41
42
43
44class build_ext(_build_ext):
45    def run(self):
46        """Build extensions in build directory, then copy if --inplace"""
47        old_inplace, self.inplace = self.inplace, 0
48        _build_ext.run(self)
49        self.inplace = old_inplace
50        if old_inplace:
51            self.copy_extensions_to_source()
52
53    def copy_extensions_to_source(self):
54        build_py = self.get_finalized_command('build_py')
55        for ext in self.extensions:
56            fullname = self.get_ext_fullname(ext.name)
57            filename = self.get_ext_filename(fullname)
58            modpath = fullname.split('.')
59            package = '.'.join(modpath[:-1])
60            package_dir = build_py.get_package_dir(package)
61            dest_filename = os.path.join(package_dir,os.path.basename(filename))
62            src_filename = os.path.join(self.build_lib,filename)
63
64            # Always copy, even if source is older than destination, to ensure
65            # that the right extensions for the current Python/platform are
66            # used.
67            copy_file(
68                src_filename, dest_filename, verbose=self.verbose,
69                dry_run=self.dry_run
70            )
71            if ext._needs_stub:
72                self.write_stub(package_dir or os.curdir, ext, True)
73
74
75    if _build_ext is not _du_build_ext and not hasattr(_build_ext,'pyrex_sources'):
76        # Workaround for problems using some Pyrex versions w/SWIG and/or 2.4
77        def swig_sources(self, sources, *otherargs):
78            # first do any Pyrex processing
79            sources = _build_ext.swig_sources(self, sources) or sources
80            # Then do any actual SWIG stuff on the remainder
81            return _du_build_ext.swig_sources(self, sources, *otherargs)
82
83
84
85    def get_ext_filename(self, fullname):
86        filename = _build_ext.get_ext_filename(self,fullname)
87        ext = self.ext_map[fullname]
88        if isinstance(ext,Library):
89            fn, ext = os.path.splitext(filename)
90            return self.shlib_compiler.library_filename(fn,libtype)
91        elif use_stubs and ext._links_to_dynamic:
92            d,fn = os.path.split(filename)
93            return os.path.join(d,'dl-'+fn)
94        else:
95            return filename
96
97    def initialize_options(self):
98        _build_ext.initialize_options(self)
99        self.shlib_compiler = None
100        self.shlibs = []
101        self.ext_map = {}
102
103    def finalize_options(self):
104        _build_ext.finalize_options(self)
105        self.extensions = self.extensions or []
106        self.check_extensions_list(self.extensions)
107        self.shlibs = [ext for ext in self.extensions
108                        if isinstance(ext,Library)]
109        if self.shlibs:
110            self.setup_shlib_compiler()
111        for ext in self.extensions:
112            ext._full_name = self.get_ext_fullname(ext.name)
113        for ext in self.extensions:
114            fullname = ext._full_name
115            self.ext_map[fullname] = ext
116            ltd = ext._links_to_dynamic = \
117                self.shlibs and self.links_to_dynamic(ext) or False
118            ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library)
119            filename = ext._file_name = self.get_ext_filename(fullname)
120            libdir = os.path.dirname(os.path.join(self.build_lib,filename))
121            if ltd and libdir not in ext.library_dirs:
122                ext.library_dirs.append(libdir)
123            if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs:
124                ext.runtime_library_dirs.append(os.curdir)
125
126    def setup_shlib_compiler(self):
127        compiler = self.shlib_compiler = new_compiler(
128            compiler=self.compiler, dry_run=self.dry_run, force=self.force
129        )
130        if sys.platform == "darwin":
131            tmp = _config_vars.copy()
132            try:
133                # XXX Help!  I don't have any idea whether these are right...
134                _config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup"
135                _config_vars['CCSHARED'] = " -dynamiclib"
136                _config_vars['SO'] = ".dylib"
137                customize_compiler(compiler)
138            finally:
139                _config_vars.clear()
140                _config_vars.update(tmp)
141        else:
142            customize_compiler(compiler)
143
144        if self.include_dirs is not None:
145            compiler.set_include_dirs(self.include_dirs)
146        if self.define is not None:
147            # 'define' option is a list of (name,value) tuples
148            for (name,value) in self.define:
149                compiler.define_macro(name, value)
150        if self.undef is not None:
151            for macro in self.undef:
152                compiler.undefine_macro(macro)
153        if self.libraries is not None:
154            compiler.set_libraries(self.libraries)
155        if self.library_dirs is not None:
156            compiler.set_library_dirs(self.library_dirs)
157        if self.rpath is not None:
158            compiler.set_runtime_library_dirs(self.rpath)
159        if self.link_objects is not None:
160            compiler.set_link_objects(self.link_objects)
161
162        # hack so distutils' build_extension() builds a library instead
163        compiler.link_shared_object = link_shared_object.__get__(compiler)
164
165
166
167    def get_export_symbols(self, ext):
168        if isinstance(ext,Library):
169            return ext.export_symbols
170        return _build_ext.get_export_symbols(self,ext)
171
172    def build_extension(self, ext):
173        _compiler = self.compiler
174        try:
175            if isinstance(ext,Library):
176                self.compiler = self.shlib_compiler
177            _build_ext.build_extension(self,ext)
178            if ext._needs_stub:
179                self.write_stub(
180                    self.get_finalized_command('build_py').build_lib, ext
181                )
182        finally:
183            self.compiler = _compiler
184
185    def links_to_dynamic(self, ext):
186        """Return true if 'ext' links to a dynamic lib in the same package"""
187        # XXX this should check to ensure the lib is actually being built
188        # XXX as dynamic, and not just using a locally-found version or a
189        # XXX static-compiled version
190        libnames = dict.fromkeys([lib._full_name for lib in self.shlibs])
191        pkg = '.'.join(ext._full_name.split('.')[:-1]+[''])
192        for libname in ext.libraries:
193            if pkg+libname in libnames: return True
194        return False
195
196    def get_outputs(self):
197        outputs = _build_ext.get_outputs(self)
198        optimize = self.get_finalized_command('build_py').optimize
199        for ext in self.extensions:
200            if ext._needs_stub:
201                base = os.path.join(self.build_lib, *ext._full_name.split('.'))
202                outputs.append(base+'.py')
203                outputs.append(base+'.pyc')
204                if optimize:
205                    outputs.append(base+'.pyo')
206        return outputs
207
208    def write_stub(self, output_dir, ext, compile=False):
209        log.info("writing stub loader for %s to %s",ext._full_name, output_dir)
210        stub_file = os.path.join(output_dir, *ext._full_name.split('.'))+'.py'
211        if compile and os.path.exists(stub_file):
212            raise DistutilsError(stub_file+" already exists! Please delete.")
213        if not self.dry_run:
214            f = open(stub_file,'w')
215            f.write('\n'.join([
216                "def __bootstrap__():",
217                "   global __bootstrap__, __file__, __loader__",
218                "   import sys, os, pkg_resources, imp"+if_dl(", dl"),
219                "   __file__ = pkg_resources.resource_filename(__name__,%r)"
220                   % os.path.basename(ext._file_name),
221                "   del __bootstrap__",
222                "   if '__loader__' in globals():",
223                "       del __loader__",
224                if_dl("   old_flags = sys.getdlopenflags()"),
225                "   old_dir = os.getcwd()",
226                "   try:",
227                "     os.chdir(os.path.dirname(__file__))",
228                if_dl("     sys.setdlopenflags(dl.RTLD_NOW)"),
229                "     imp.load_dynamic(__name__,__file__)",
230                "   finally:",
231                if_dl("     sys.setdlopenflags(old_flags)"),
232                "     os.chdir(old_dir)",
233                "__bootstrap__()",
234                "" # terminal \n
235            ]))
236            f.close()
237        if compile:
238            from distutils.util import byte_compile
239            byte_compile([stub_file], optimize=0,
240                         force=True, dry_run=self.dry_run)
241            optimize = self.get_finalized_command('install_lib').optimize
242            if optimize > 0:
243                byte_compile([stub_file], optimize=optimize,
244                             force=True, dry_run=self.dry_run)
245            if os.path.exists(stub_file) and not self.dry_run:
246                os.unlink(stub_file)
247
248
249if use_stubs or os.name=='nt':
250    # Build shared libraries
251    #
252    def link_shared_object(self, objects, output_libname, output_dir=None,
253        libraries=None, library_dirs=None, runtime_library_dirs=None,
254        export_symbols=None, debug=0, extra_preargs=None,
255        extra_postargs=None, build_temp=None, target_lang=None
256    ):  self.link(
257            self.SHARED_LIBRARY, objects, output_libname,
258            output_dir, libraries, library_dirs, runtime_library_dirs,
259            export_symbols, debug, extra_preargs, extra_postargs,
260            build_temp, target_lang
261        )
262else:
263    # Build static libraries everywhere else
264    libtype = 'static'
265
266    def link_shared_object(self, objects, output_libname, output_dir=None,
267        libraries=None, library_dirs=None, runtime_library_dirs=None,
268        export_symbols=None, debug=0, extra_preargs=None,
269        extra_postargs=None, build_temp=None, target_lang=None
270    ):
271        # XXX we need to either disallow these attrs on Library instances,
272        #     or warn/abort here if set, or something...
273        #libraries=None, library_dirs=None, runtime_library_dirs=None,
274        #export_symbols=None, extra_preargs=None, extra_postargs=None,
275        #build_temp=None
276
277        assert output_dir is None   # distutils build_ext doesn't pass this
278        output_dir,filename = os.path.split(output_libname)
279        basename, ext = os.path.splitext(filename)
280        if self.library_filename("x").startswith('lib'):
281            # strip 'lib' prefix; this is kludgy if some platform uses
282            # a different prefix
283            basename = basename[3:]
284
285        self.create_static_lib(
286            objects, basename, output_dir, debug, target_lang
287        )
288