~ubuntu-branches/ubuntu/jaunty/calibre/jaunty-backports

« back to all changes in this revision

Viewing changes to pyqtdistutils.py

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2009-01-20 17:14:02 UTC
  • Revision ID: james.westby@ubuntu.com-20090120171402-8y3znf6nokwqe80k
Tags: upstream-0.4.125+dfsg
ImportĀ upstreamĀ versionĀ 0.4.125+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env  python
 
2
__license__   = 'GPL v3'
 
3
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
 
4
__docformat__ = 'restructuredtext en'
 
5
 
 
6
'''
 
7
Build PyQt extensions. Integrates with distutils (but uses the PyQt build system).
 
8
'''
 
9
from distutils.core import Extension as _Extension
 
10
from distutils.command.build_ext import build_ext as _build_ext
 
11
from distutils.dep_util import newer_group
 
12
from distutils import log
 
13
 
 
14
import sipconfig, os, sys, string, glob, shutil
 
15
from PyQt4 import pyqtconfig
 
16
iswindows = 'win32' in sys.platform
 
17
QMAKE = os.path.expanduser('~/qt/bin/qmake') if 'darwin' in sys.platform else'qmake'
 
18
WINDOWS_PYTHON = ['C:/Python26/libs']
 
19
OSX_SDK = '/Developer/SDKs/MacOSX10.4u.sdk'
 
20
 
 
21
def replace_suffix(path, new_suffix):
 
22
    return os.path.splitext(path)[0] + new_suffix
 
23
 
 
24
class Extension(_Extension):
 
25
    pass
 
26
 
 
27
if iswindows:
 
28
    from distutils import msvc9compiler
 
29
    msvc = msvc9compiler.MSVCCompiler()
 
30
    msvc.initialize()
 
31
    nmake = msvc.find_exe('nmake.exe')
 
32
    rc = msvc.find_exe('rc.exe')
 
33
 
 
34
 
 
35
class PyQtExtension(Extension):
 
36
 
 
37
    def __init__(self, name, sources, sip_sources, **kw):
 
38
        '''
 
39
        :param sources: Qt .cpp and .h files needed for this extension
 
40
        :param sip_sources: List of .sip files this extension depends on. The
 
41
                            first .sip file will be used toactually build the extension.
 
42
        '''
 
43
        self.module_makefile = pyqtconfig.QtGuiModuleMakefile
 
44
        self.sip_sources = map(lambda x: x.replace('/', os.sep), sip_sources)
 
45
        Extension.__init__(self, name, sources, **kw)
 
46
 
 
47
 
 
48
class build_ext(_build_ext):
 
49
 
 
50
    def make(self, makefile):
 
51
        make = nmake if iswindows else 'make'
 
52
        self.spawn([make, '-f', makefile])
 
53
 
 
54
    def build_qt_objects(self, ext, bdir):
 
55
        if not iswindows:
 
56
            bdir = os.path.join(bdir, 'qt')
 
57
        if not os.path.exists(bdir):
 
58
            os.makedirs(bdir)
 
59
        cwd = os.getcwd()
 
60
        sources = map(os.path.abspath, ext.sources)
 
61
        os.chdir(bdir)
 
62
        try:
 
63
            headers = set([f for f in sources if f.endswith('.h')])
 
64
            sources = set(sources) - headers
 
65
            name = ext.name.rpartition('.')[-1]
 
66
            pro = '''\
 
67
TARGET   = %s
 
68
TEMPLATE = lib
 
69
HEADERS  = %s
 
70
SOURCES  = %s
 
71
VERSION  = 1.0.0
 
72
CONFIG   += x86 ppc
 
73
'''%(name, ' '.join(headers), ' '.join(sources))
 
74
            open(name+'.pro', 'wb').write(pro)
 
75
            self.spawn([QMAKE, '-o', 'Makefile.qt', name+'.pro'])
 
76
            self.make('Makefile.qt')
 
77
            pat = 'release\\*.obj' if iswindows else '*.o'
 
78
            return map(os.path.abspath, glob.glob(pat))
 
79
        finally:
 
80
            os.chdir(cwd)
 
81
 
 
82
    def build_sbf(self, sip, sbf, bdir):
 
83
        print '\tBuilding spf...'
 
84
        sip_bin = self.sipcfg.sip_bin
 
85
        self.spawn([sip_bin,
 
86
                    "-c", bdir,
 
87
                    "-b", sbf,
 
88
                    '-I', self.pyqtcfg.pyqt_sip_dir,
 
89
                    ] + self.pyqtcfg.pyqt_sip_flags.split()+
 
90
                    [sip])
 
91
 
 
92
    def build_pyqt(self, bdir, sbf, ext, qtobjs, headers):
 
93
        makefile = ext.module_makefile(configuration=self.pyqtcfg,
 
94
                                       build_file=sbf, dir=bdir,
 
95
                                       makefile='Makefile.pyqt',
 
96
                                       universal=OSX_SDK, qt=1)
 
97
        if 'win32' in sys.platform:
 
98
            makefile.extra_lib_dirs += WINDOWS_PYTHON
 
99
        makefile.extra_include_dirs = list(set(map(os.path.dirname, headers)))
 
100
        makefile.extra_lflags += qtobjs
 
101
        makefile.generate()
 
102
        cwd = os.getcwd()
 
103
        os.chdir(bdir)
 
104
        try:
 
105
            self.make('Makefile.pyqt')
 
106
        finally:
 
107
            os.chdir(cwd)
 
108
 
 
109
 
 
110
 
 
111
    def build_extension(self, ext):
 
112
        self.inplace = True # Causes extensions to be built in the source tree
 
113
        
 
114
        fullname = self.get_ext_fullname(ext.name)
 
115
        if self.inplace:
 
116
            # ignore build-lib -- put the compiled extension into
 
117
            # the source tree along with pure Python modules
 
118
 
 
119
            modpath = string.split(fullname, '.')
 
120
            package = string.join(modpath[0:-1], '.')
 
121
            base = modpath[-1]
 
122
 
 
123
            build_py = self.get_finalized_command('build_py')
 
124
            package_dir = build_py.get_package_dir(package)
 
125
            ext_filename = os.path.join(package_dir,
 
126
                                        self.get_ext_filename(base))
 
127
        else:
 
128
            ext_filename = os.path.join(self.build_lib,
 
129
                                        self.get_ext_filename(fullname))
 
130
        bdir = os.path.abspath(os.path.join(self.build_temp, fullname))    
 
131
        if not os.path.exists(bdir):
 
132
            os.makedirs(bdir)
 
133
            
 
134
        if not isinstance(ext, PyQtExtension):
 
135
            if not iswindows:
 
136
                return _build_ext.build_extension(self, ext)
 
137
            
 
138
            c_sources = [f for f in ext.sources if os.path.splitext(f)[1].lower() in ('.c', '.cpp', '.cxx')]
 
139
            compile_args = '/c /nologo /Ox /MD /W3 /GX /DNDEBUG'.split()
 
140
            compile_args += ext.extra_compile_args
 
141
            self.swig_opts = ''
 
142
            inc_dirs = self.include_dirs + [x.replace('/', '\\') for x in ext.include_dirs]
 
143
            cc = [msvc.cc] + compile_args + ['-I%s'%x for x in list(set(inc_dirs))]
 
144
            objects = []
 
145
            for f in c_sources:
 
146
                o = os.path.join(bdir, os.path.basename(f)+'.obj')
 
147
                objects.append(o)
 
148
                compiler =  cc + ['/Tc'+f, '/Fo'+o]
 
149
                self.spawn(compiler)
 
150
            out = os.path.join(bdir, base+'.pyd') 
 
151
            linker = [msvc.linker] + '/DLL /nologo /INCREMENTAL:NO'.split()
 
152
            linker += ['/LIBPATH:'+x for x in self.library_dirs]
 
153
            linker += [x+'.lib' for x in ext.libraries]
 
154
            linker += ['/EXPORT:init'+base] + objects + ['/OUT:'+out]
 
155
            self.spawn(linker)
 
156
            for src in (out, out+'.manifest'):
 
157
                shutil.copyfile(src, os.path.join('src', 'calibre', 'plugins', os.path.basename(src)))
 
158
            return
 
159
                
 
160
        
 
161
        
 
162
        if not os.path.exists(bdir):
 
163
            os.makedirs(bdir)
 
164
        ext.sources2 = map(os.path.abspath, ext.sources)
 
165
        qt_dir = 'qt\\release' if iswindows else 'qt'
 
166
        objects = set(map(lambda x: os.path.join(bdir, qt_dir, replace_suffix(os.path.basename(x), '.o')),
 
167
                      [s for s in ext.sources2 if not s.endswith('.h')]))
 
168
        newer = False
 
169
        for object in objects:
 
170
            if newer_group(ext.sources2, object, missing='newer'):
 
171
                newer = True
 
172
                break
 
173
        headers = [f for f in ext.sources2 if f.endswith('.h')]
 
174
        if self.force or newer:
 
175
            log.info('building \'%s\' extension', ext.name)
 
176
            objects = self.build_qt_objects(ext, bdir)
 
177
 
 
178
        self.sipcfg  = sipconfig.Configuration()
 
179
        self.pyqtcfg = pyqtconfig.Configuration()
 
180
        sbf_sources = []
 
181
        for sip in ext.sip_sources:
 
182
            sipbasename = os.path.basename(sip)
 
183
            sbf = os.path.join(bdir, replace_suffix(sipbasename, ".sbf"))
 
184
            sbf_sources.append(sbf)
 
185
            if self.force or newer_group(ext.sip_sources, sbf, 'newer'):
 
186
                self.build_sbf(sip, sbf, bdir)
 
187
        generated_sources = []
 
188
        for sbf in sbf_sources:
 
189
            generated_sources += self.get_sip_output_list(sbf, bdir)
 
190
 
 
191
        depends = generated_sources + list(objects)
 
192
        mod = os.path.join(bdir, os.path.basename(ext_filename))
 
193
 
 
194
        if self.force or newer_group(depends, mod, 'newer'):
 
195
            self.build_pyqt(bdir, sbf_sources[0], ext, list(objects), headers)
 
196
 
 
197
        if self.force or newer_group([mod], ext_filename, 'newer'):
 
198
            if os.path.exists(ext_filename):
 
199
                os.unlink(ext_filename)
 
200
            shutil.copyfile(mod, ext_filename)
 
201
            shutil.copymode(mod, ext_filename)
 
202
 
 
203
    def get_sip_output_list(self, sbf, bdir):
 
204
        """
 
205
        Parse the sbf file specified to extract the name of the generated source
 
206
        files. Make them absolute assuming they reside in the temp directory.
 
207
        """
 
208
        for L in file(sbf):
 
209
            key, value = L.split("=", 1)
 
210
            if key.strip() == "sources":
 
211
                out = []
 
212
                for o in value.split():
 
213
                    out.append(os.path.join(bdir, o))
 
214
                return out
 
215
 
 
216
        raise RuntimeError, "cannot parse SIP-generated '%s'" % sbf
 
217
 
 
218
    def run_sip(self, sip_files):
 
219
        sip_bin = self.sipcfg.sip_bin
 
220
        sip_sources = [i[0] for i in sip_files]
 
221
        generated_sources = []
 
222
        for sip, sbf in sip_files:
 
223
            if not (self.force or newer_group(sip_sources, sbf, 'newer')):
 
224
                log.info(sbf + ' is up to date')
 
225
                continue
 
226
            self.spawn([sip_bin,
 
227
                    "-c", self.build_temp,
 
228
                    "-b", sbf,
 
229
                    '-I', self.pyqtcfg.pyqt_sip_dir,
 
230
                    ] + self.pyqtcfg.pyqt_sip_flags.split()+
 
231
                    [sip])
 
232
            generated_sources += self.get_sip_output_list(sbf)
 
233
        return generated_sources
 
234