mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
195 lines
7.3 KiB
Python
195 lines
7.3 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
|
|
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
|
|
WINDOWS_PYTHON = ['C:/Python25/libs']
|
|
OSX_SDK = 'TODO:'
|
|
|
|
def replace_suffix(path, new_suffix):
|
|
return os.path.splitext(path)[0] + new_suffix
|
|
|
|
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 = 'make'
|
|
if iswindows:
|
|
make = 'mingw32-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\\*.o' if iswindows else '*.o'
|
|
return glob.glob(pat)
|
|
finally:
|
|
os.chdir(cwd)
|
|
|
|
def build_sbf(self, sip, sbf, bdir):
|
|
sip_bin = self.sipcfg.sip_bin
|
|
self.spawn([sip_bin,
|
|
"-c", bdir,
|
|
"-b", sbf,
|
|
'-I', self.pyqtcfg.pyqt_sip_dir,
|
|
] + self.pyqtcfg.pyqt_sip_flags.split()+
|
|
[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)
|
|
if 'win32' in sys.platform:
|
|
makefile.extra_lib_dirs += WINDOWS_PYTHON
|
|
makefile.extra_include_dirs = list(set(map(os.path.dirname, headers)))
|
|
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
|
|
if not isinstance(ext, PyQtExtension):
|
|
return _build_ext.build_extension(self, ext)
|
|
|
|
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)
|
|
ext.sources = 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.sources if not s.endswith('.h')]))
|
|
newer = False
|
|
for object in objects:
|
|
if newer_group(ext.sources, object, missing='newer'):
|
|
newer = True
|
|
break
|
|
headers = [f for f in ext.sources 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)
|
|
|
|
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
|
|
|
|
|