import os, re, sys
from distutils import util
from distutils.command import build_ext
from distutils.dep_util import newer_group, newer

class BuildExt(build_ext.build_ext):

    command_name = 'build_ext'

    _include_re = re.compile(r'^\s*#\s*include\s*("|<)(?P<file>[^">]+?)("|>)',
                             re.MULTILINE)

    def _find_includes(self, ext, filename, includes):
        source = open(filename).read()
        basename = os.path.basename(filename)

        include_dirs = [os.path.dirname(filename)]
        include_dirs.extend(ext.include_dirs)

        match = self._include_re.search(source)
        while match:
            filename = util.convert_path(match.group('file'))
            for base in include_dirs:
                include = os.path.normpath(os.path.join(base, filename))
                if os.path.isfile(include) and include not in includes:
                    includes.append(include)
                    self._find_includes(ext, include, includes)
                    break
            match = self._include_re.search(source, match.end())
        return

    def get_source_files (self):
        self.check_extensions_list(self.extensions)
        filenames = []

        for ext in self.extensions:
            all_sources = self.prep_build(ext)
            for source, includes in all_sources:
                filenames.append(source)
                filenames.extend(includes)

        return filenames

    def prep_build(self, ext):
        # This should really exist in the CCompiler class, but
        # that would required overriding all compilers.
        result = []
        for source in ext.sources:
            includes = []
            self._find_includes(ext, util.convert_path(source), includes)
            result.append((source, includes))
        return result
        
    def build_extension(self, ext):
        fullname = self.get_ext_fullname(ext.name)
        ext_filename = os.path.join(self.build_lib,
                                    self.get_ext_filename(fullname))

        all_sources = self.prep_build(ext)
        
        sources = []
        for source, includes in all_sources:
            sources.append(source)
            sources.extend(includes)

        if not (self.force or newer_group(sources, ext_filename, 'newer')):
            self.announce("skipping '%s' extension (up-to-date)" %
                          ext.name)
            return
        else:
            self.announce("building '%s' extension" % ext.name)

        preargs = []
        postargs = ext.extra_compile_args or []
        
        macros = ext.define_macros[:]
        for undef in ext.undef_macros:
            macros.append((undef,))

        objects = []
        for source, includes in all_sources:
            kwords = {}
            if sys.hexversion > 0x02030000:
                output_dir = self.build_temp
            else:
                output_dir = os.path.join(self.build_temp,
                                          os.path.dirname(source))
                
            if sys.hexversion > 0x02030000:
                # Distutils has a depends keyword for compiling
                kwords['depends'] = includes
            elif not self.force:
                # Recompile if the includes or source are newer than the
                # resulting object files.
                objs = self.compiler.object_filenames([source], 1, output_dir)
                
                # Recompile if any of the inputs are newer than the object
                inputs = [source] + includes
                force = 0
                for filename in objs:
                    force = force or newer_group(inputs, filename, 'newer')
                self.compiler.force = force

            objs = self.compiler.compile([source],
                                         output_dir=output_dir,
                                         macros=macros,
                                         include_dirs=ext.include_dirs,
                                         debug=self.debug,
                                         extra_preargs=preargs,
                                         extra_postargs=postargs,
                                         **kwords)
            objects.extend(objs)

        # Reset the force flag on the compilier
        self.compiler.force = self.force
        
        # Now link the object files together into a "shared object" --
        # of course, first we have to figure out all the other things
        # that go into the mix.
        if ext.extra_objects:
            objects.extend(ext.extra_objects)

        extra_args = ext.extra_link_args or []

        self.compiler.link_shared_object(
            objects, ext_filename,
            libraries=self.get_libraries(ext),
            library_dirs=ext.library_dirs,
            runtime_library_dirs=ext.runtime_library_dirs,
            extra_preargs=extra_args,
            extra_postargs=[],
            export_symbols=self.get_export_symbols(ext),
            debug=self.debug,
            build_temp=self.build_temp)
        return
