from distutils.command.build_ext import build_ext as _du_build_ext
try:
    # Attempt to use Pyrex for building extensions, if available
    from Pyrex.Distutils.build_ext import build_ext as _build_ext
except ImportError:
    _build_ext = _du_build_ext

import os, sys
from distutils.file_util import copy_file
from setuptools.extension import Library
from distutils.ccompiler import new_compiler
from distutils.sysconfig import customize_compiler, get_config_var
get_config_var("LDSHARED")  # make sure _config_vars is initialized
from distutils.sysconfig import _config_vars
from distutils import log
from distutils.errors import *

have_rtld = False
use_stubs = False
libtype = 'shared'

if sys.platform == "darwin":
    use_stubs = True
elif os.name != 'nt':
    try:
        from dl import RTLD_NOW
        have_rtld = True
        use_stubs = True
    except ImportError:
        pass


def if_dl(s):
    if have_rtld:
        return s
    return ''



class build_ext(_build_ext):
    def run(self):
        """Build extensions in build directory, then copy if --inplace"""
        old_inplace, self.inplace = self.inplace, 0
        _build_ext.run(self)
        self.inplace = old_inplace
        if old_inplace:
            self.copy_extensions_to_source()

    def copy_extensions_to_source(self):
        build_py = self.get_finalized_command('build_py')
        for ext in self.extensions:
            fullname = self.get_ext_fullname(ext.name)
            filename = self.get_ext_filename(fullname)
            modpath = fullname.split('.')
            package = '.'.join(modpath[:-1])
            package_dir = build_py.get_package_dir(package)
            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
            # used.
            copy_file(
                src_filename, dest_filename, verbose=self.verbose,
                dry_run=self.dry_run
            )
            if ext._needs_stub:
                self.write_stub(package_dir or os.curdir, ext, True)


    if _build_ext is not _du_build_ext and \
       not hasattr(_build_ext,'pyrex_sources'):

        # Workaround for problems using some Pyrex versions w/SWIG and/or 2.4
        def swig_sources(self, sources, *otherargs):
            # first do any Pyrex processing
            sources = _build_ext.swig_sources(self, sources) or sources
            # 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)
        ext = self.ext_map[fullname]
        if isinstance(ext,Library):
            fn, ext = os.path.splitext(filename)
            return self.shlib_compiler.library_filename(fn,libtype)
        elif use_stubs and ext._links_to_dynamic:
            d,fn = os.path.split(filename)
            return os.path.join(d,'dl-'+fn)
        else:
            return filename


    def initialize_options(self):
        _build_ext.initialize_options(self)
        self.shlib_compiler = None
        self.shlibs = []
        self.ext_map = {}


    def finalize_options(self):
        _build_ext.finalize_options(self)
        self.extensions = self.extensions or []
        self.check_extensions_list(self.extensions)
        self.shlibs = [ext for ext in self.extensions
                       if isinstance(ext,Library)]
        if self.shlibs:
            self.setup_shlib_compiler()

        for ext in self.extensions:
            ext._full_name = self.get_ext_fullname(ext.name)

        for ext in self.extensions:
            fullname = ext._full_name
            self.ext_map[fullname] = ext
            ltd = ext._links_to_dynamic = \
                self.shlibs and self.links_to_dynamic(ext) or False
            ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library)
            filename = ext._file_name = self.get_ext_filename(fullname)
            libdir = os.path.dirname(os.path.join(self.build_lib,filename))

            if ltd and libdir not in ext.library_dirs:
                ext.library_dirs.append(libdir)

            if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs:
                ext.runtime_library_dirs.append(os.curdir)


    def setup_shlib_compiler(self):
        compiler = self.shlib_compiler = new_compiler(
            compiler=self.compiler, dry_run=self.dry_run, force=self.force
        )
        if sys.platform == "darwin":
            tmp = _config_vars.copy()
            try:
                # XXX Help!  I don't have any idea whether these are right...
                _config_vars['LDSHARED'] = \
                    "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup"
                _config_vars['CCSHARED'] = " -dynamiclib"
                _config_vars['SO'] = ".dylib"
                customize_compiler(compiler)
            finally:
                _config_vars.clear()
                _config_vars.update(tmp)
        else:
            customize_compiler(compiler)

        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 library instead
        compiler.link_shared_object = link_shared_object.__get__(compiler)


    def get_export_symbols(self, ext):
        if isinstance(ext,Library):
            return ext.export_symbols
        return _build_ext.get_export_symbols(self,ext)


    def build_extension(self, ext):
        _compiler = self.compiler
        try:
            if isinstance(ext,Library):
                self.compiler = self.shlib_compiler
            _build_ext.build_extension(self,ext)
            if ext._needs_stub:
                self.write_stub(
                    self.get_finalized_command('build_py').build_lib, ext
                )
        finally:
            self.compiler = _compiler


    def links_to_dynamic(self, ext):
        """Return true if 'ext' links to a dynamic lib in the same package"""
        # XXX this should check to ensure the lib is actually being built
        # XXX as dynamic, and not just using a locally-found version or a
        # XXX static-compiled version
        libnames = dict.fromkeys([lib._full_name for lib in self.shlibs])
        pkg = '.'.join(ext._full_name.split('.')[:-1]+[''])
        for libname in ext.libraries:
            if pkg+libname in libnames: return True
        return False


    def get_outputs(self):
        outputs = _build_ext.get_outputs(self)
        optimize = self.get_finalized_command('build_py').optimize