mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-04 03:27:00 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			252 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/usr/bin/env  python
 | 
						|
__license__   = 'GPL v3'
 | 
						|
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
 | 
						|
__docformat__ = 'restructuredtext en'
 | 
						|
 | 
						|
'''
 | 
						|
Build PyQt extensions. Integrates with distutils (but uses the PyQt build system).
 | 
						|
'''
 | 
						|
from distutils.core import Extension as _Extension
 | 
						|
from distutils.command.build_ext import build_ext as _build_ext
 | 
						|
from distutils.dep_util import newer_group
 | 
						|
from distutils import log
 | 
						|
 | 
						|
import sipconfig, os, sys, string, glob, shutil
 | 
						|
from PyQt4 import pyqtconfig
 | 
						|
iswindows = 'win32' in sys.platform
 | 
						|
QMAKE = os.path.expanduser('~/qt/bin/qmake') if 'darwin' in sys.platform else'qmake'
 | 
						|
WINDOWS_PYTHON = ['C:/Python26/libs']
 | 
						|
OSX_SDK = '/Developer/SDKs/MacOSX10.4u.sdk'
 | 
						|
 | 
						|
def replace_suffix(path, new_suffix):
 | 
						|
    return os.path.splitext(path)[0] + new_suffix
 | 
						|
 | 
						|
class Extension(_Extension):
 | 
						|
    pass
 | 
						|
 | 
						|
if iswindows:
 | 
						|
    from distutils import msvc9compiler
 | 
						|
    msvc = msvc9compiler.MSVCCompiler()
 | 
						|
    msvc.initialize()
 | 
						|
    nmake = msvc.find_exe('nmake.exe')
 | 
						|
    rc = msvc.find_exe('rc.exe')
 | 
						|
 | 
						|
 | 
						|
class PyQtExtension(Extension):
 | 
						|
 | 
						|
    def __init__(self, name, sources, sip_sources, **kw):
 | 
						|
        '''
 | 
						|
        :param sources: Qt .cpp and .h files needed for this extension
 | 
						|
        :param sip_sources: List of .sip files this extension depends on. The
 | 
						|
                            first .sip file will be used toactually build the extension.
 | 
						|
        '''
 | 
						|
        self.module_makefile = pyqtconfig.QtGuiModuleMakefile
 | 
						|
        self.sip_sources = map(lambda x: x.replace('/', os.sep), sip_sources)
 | 
						|
        Extension.__init__(self, name, sources, **kw)
 | 
						|
 | 
						|
 | 
						|
class build_ext(_build_ext):
 | 
						|
 | 
						|
    def make(self, makefile):
 | 
						|
        make = nmake if iswindows else 'make'
 | 
						|
        self.spawn([make, '-f', makefile])
 | 
						|
 | 
						|
    def build_qt_objects(self, ext, bdir):
 | 
						|
        if not iswindows:
 | 
						|
            bdir = os.path.join(bdir, 'qt')
 | 
						|
        if not os.path.exists(bdir):
 | 
						|
            os.makedirs(bdir)
 | 
						|
        cwd = os.getcwd()
 | 
						|
        sources = map(os.path.abspath, ext.sources)
 | 
						|
        os.chdir(bdir)
 | 
						|
        try:
 | 
						|
            headers = set([f for f in sources if f.endswith('.h')])
 | 
						|
            sources = set(sources) - headers
 | 
						|
            name = ext.name.rpartition('.')[-1]
 | 
						|
            pro = '''\
 | 
						|
TARGET   = %s
 | 
						|
TEMPLATE = lib
 | 
						|
HEADERS  = %s
 | 
						|
SOURCES  = %s
 | 
						|
VERSION  = 1.0.0
 | 
						|
CONFIG   += x86 ppc
 | 
						|
'''%(name, ' '.join(headers), ' '.join(sources))
 | 
						|
            open(name+'.pro', 'wb').write(pro)
 | 
						|
            self.spawn([QMAKE, '-o', 'Makefile.qt', name+'.pro'])
 | 
						|
            self.make('Makefile.qt')
 | 
						|
            pat = 'release\\*.obj' if iswindows else '*.o'
 | 
						|
            return map(os.path.abspath, glob.glob(pat))
 | 
						|
        finally:
 | 
						|
            os.chdir(cwd)
 | 
						|
 | 
						|
    def build_sbf(self, sip, sbf, bdir):
 | 
						|
        print '\tBuilding sbf...'
 | 
						|
        sip_bin = self.sipcfg.sip_bin
 | 
						|
        pyqt_sip_flags = []
 | 
						|
        if hasattr(self, 'pyqtcfg'):
 | 
						|
            pyqt_sip_flags += ['-I', self.pyqtcfg.pyqt_sip_dir]
 | 
						|
            pyqt_sip_flags += self.pyqtcfg.pyqt_sip_flags.split()
 | 
						|
        self.spawn([sip_bin,
 | 
						|
                    "-c", bdir,
 | 
						|
                    "-b", sbf,
 | 
						|
                    ] + pyqt_sip_flags +
 | 
						|
                    [sip])
 | 
						|
 | 
						|
    def build_pyqt(self, bdir, sbf, ext, qtobjs, headers):
 | 
						|
        makefile = ext.module_makefile(configuration=self.pyqtcfg,
 | 
						|
                                       build_file=sbf, dir=bdir,
 | 
						|
                                       makefile='Makefile.pyqt',
 | 
						|
                                       universal=OSX_SDK, qt=1)
 | 
						|
        makefile.extra_libs = ext.libraries
 | 
						|
        makefile.extra_lib_dirs = ext.library_dirs
 | 
						|
        makefile.extra_cxxflags = ext.extra_compile_args
 | 
						|
 | 
						|
        if 'win32' in sys.platform:
 | 
						|
            makefile.extra_lib_dirs += WINDOWS_PYTHON
 | 
						|
        makefile.extra_include_dirs = list(set(map(os.path.dirname, headers)))
 | 
						|
        makefile.extra_include_dirs += ext.include_dirs
 | 
						|
        makefile.extra_lflags += qtobjs
 | 
						|
        makefile.generate()
 | 
						|
        cwd = os.getcwd()
 | 
						|
        os.chdir(bdir)
 | 
						|
        try:
 | 
						|
            self.make('Makefile.pyqt')
 | 
						|
        finally:
 | 
						|
            os.chdir(cwd)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
    def build_extension(self, ext):
 | 
						|
        self.inplace = True # Causes extensions to be built in the source tree
 | 
						|
 | 
						|
        fullname = self.get_ext_fullname(ext.name)
 | 
						|
        if self.inplace:
 | 
						|
            # ignore build-lib -- put the compiled extension into
 | 
						|
            # the source tree along with pure Python modules
 | 
						|
 | 
						|
            modpath = string.split(fullname, '.')
 | 
						|
            package = string.join(modpath[0:-1], '.')
 | 
						|
            base = modpath[-1]
 | 
						|
 | 
						|
            build_py = self.get_finalized_command('build_py')
 | 
						|
            package_dir = build_py.get_package_dir(package)
 | 
						|
            ext_filename = os.path.join(package_dir,
 | 
						|
                                        self.get_ext_filename(base))
 | 
						|
        else:
 | 
						|
            ext_filename = os.path.join(self.build_lib,
 | 
						|
                                        self.get_ext_filename(fullname))
 | 
						|
        bdir = os.path.abspath(os.path.join(self.build_temp, fullname))
 | 
						|
        if not os.path.exists(bdir):
 | 
						|
            os.makedirs(bdir)
 | 
						|
 | 
						|
        if not isinstance(ext, PyQtExtension):
 | 
						|
            if not iswindows:
 | 
						|
                return _build_ext.build_extension(self, ext)
 | 
						|
 | 
						|
            c_sources = [f for f in ext.sources if os.path.splitext(f)[1].lower() in ('.c', '.cpp', '.cxx')]
 | 
						|
            compile_args = '/c /nologo /Ox /MD /W3 /EHsc /DNDEBUG'.split()
 | 
						|
            compile_args += ext.extra_compile_args
 | 
						|
            self.swig_opts = ''
 | 
						|
            inc_dirs = self.include_dirs + [x.replace('/', '\\') for x in ext.include_dirs]
 | 
						|
            cc = [msvc.cc] + compile_args + ['-I%s'%x for x in list(set(inc_dirs))]
 | 
						|
            objects = []
 | 
						|
            for f in c_sources:
 | 
						|
                o = os.path.join(bdir, os.path.basename(f)+'.obj')
 | 
						|
                objects.append(o)
 | 
						|
                inf = '/Tp' if f.endswith('.cpp') else '/Tc'
 | 
						|
                compiler =  cc + [inf+f, '/Fo'+o]
 | 
						|
                self.spawn(compiler)
 | 
						|
            out = os.path.join(bdir, base+'.pyd')
 | 
						|
            linker = [msvc.linker] + '/DLL /nologo /INCREMENTAL:NO'.split()
 | 
						|
            linker += ['/LIBPATH:'+x for x in self.library_dirs+ext.library_dirs]
 | 
						|
            linker += [x+'.lib' for x in ext.libraries]
 | 
						|
            linker += ['/EXPORT:init'+base] + objects + ['/OUT:'+out]
 | 
						|
            self.spawn(linker)
 | 
						|
            for src in (out, out+'.manifest'):
 | 
						|
                shutil.copyfile(src, os.path.join('src', 'calibre', 'plugins', os.path.basename(src)))
 | 
						|
            return
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        if not os.path.exists(bdir):
 | 
						|
            os.makedirs(bdir)
 | 
						|
        ext.sources2 = map(os.path.abspath, ext.sources)
 | 
						|
        qt_dir = 'qt\\release' if iswindows else 'qt'
 | 
						|
        objects = set(map(lambda x: os.path.join(bdir, qt_dir, replace_suffix(os.path.basename(x), '.o')),
 | 
						|
                      [s for s in ext.sources2 if not s.endswith('.h')]))
 | 
						|
        newer = False
 | 
						|
        for object in objects:
 | 
						|
            if newer_group(ext.sources2, object, missing='newer'):
 | 
						|
                newer = True
 | 
						|
                break
 | 
						|
        headers = [f for f in ext.sources2 if f.endswith('.h')]
 | 
						|
        if self.force or newer:
 | 
						|
            log.info('building \'%s\' extension', ext.name)
 | 
						|
            objects = self.build_qt_objects(ext, bdir)
 | 
						|
 | 
						|
        self.sipcfg  = sipconfig.Configuration()
 | 
						|
        self.pyqtcfg = pyqtconfig.Configuration()
 | 
						|
        sbf_sources = []
 | 
						|
        for sip in ext.sip_sources:
 | 
						|
            sipbasename = os.path.basename(sip)
 | 
						|
            sbf = os.path.join(bdir, replace_suffix(sipbasename, ".sbf"))
 | 
						|
            sbf_sources.append(sbf)
 | 
						|
            if self.force or newer_group(ext.sip_sources, sbf, 'newer'):
 | 
						|
                self.build_sbf(sip, sbf, bdir)
 | 
						|
        generated_sources = []
 | 
						|
        for sbf in sbf_sources:
 | 
						|
            generated_sources += self.get_sip_output_list(sbf, bdir)
 | 
						|
 | 
						|
        depends = generated_sources + list(objects)
 | 
						|
        mod = os.path.join(bdir, os.path.basename(ext_filename))
 | 
						|
 | 
						|
        if self.force or newer_group(depends, mod, 'newer'):
 | 
						|
            self.build_pyqt(bdir, sbf_sources[0], ext, list(objects), headers)
 | 
						|
 | 
						|
        if self.force or newer_group([mod], ext_filename, 'newer'):
 | 
						|
            if os.path.exists(ext_filename):
 | 
						|
                os.unlink(ext_filename)
 | 
						|
            shutil.copyfile(mod, ext_filename)
 | 
						|
            shutil.copymode(mod, ext_filename)
 | 
						|
 | 
						|
 | 
						|
        if self.force or newer_group([mod], ext_filename, 'newer'):
 | 
						|
            if os.path.exists(ext_filename):
 | 
						|
                os.unlink(ext_filename)
 | 
						|
            shutil.copyfile(mod, ext_filename)
 | 
						|
            shutil.copymode(mod, ext_filename)
 | 
						|
 | 
						|
 | 
						|
    def get_sip_output_list(self, sbf, bdir):
 | 
						|
        """
 | 
						|
        Parse the sbf file specified to extract the name of the generated source
 | 
						|
        files. Make them absolute assuming they reside in the temp directory.
 | 
						|
        """
 | 
						|
        for L in file(sbf):
 | 
						|
            key, value = L.split("=", 1)
 | 
						|
            if key.strip() == "sources":
 | 
						|
                out = []
 | 
						|
                for o in value.split():
 | 
						|
                    out.append(os.path.join(bdir, o))
 | 
						|
                return out
 | 
						|
 | 
						|
        raise RuntimeError, "cannot parse SIP-generated '%s'" % sbf
 | 
						|
 | 
						|
    def run_sip(self, sip_files):
 | 
						|
        sip_bin = self.sipcfg.sip_bin
 | 
						|
        sip_sources = [i[0] for i in sip_files]
 | 
						|
        generated_sources = []
 | 
						|
        for sip, sbf in sip_files:
 | 
						|
            if not (self.force or newer_group(sip_sources, sbf, 'newer')):
 | 
						|
                log.info(sbf + ' is up to date')
 | 
						|
                continue
 | 
						|
            self.spawn([sip_bin,
 | 
						|
                    "-c", self.build_temp,
 | 
						|
                    "-b", sbf,
 | 
						|
                    '-I', self.pyqtcfg.pyqt_sip_dir,
 | 
						|
                    ] + self.pyqtcfg.pyqt_sip_flags.split()+
 | 
						|
                    [sip])
 | 
						|
            generated_sources += self.get_sip_output_list(sbf)
 | 
						|
        return generated_sources
 | 
						|
 |