pyqtdistutils.py 4.94 KB
Newer Older
1 2 3 4 5 6
# Subclasses disutils.command.build_ext,
# replacing it with a SIP version that compiles .sip -> .cpp
# before calling the original build_ext command.
# Written by Giovanni Bajo <rasky at develer dot com>
# Based on Pyrex.Distutils, written by Graham Fawcett and Darrel Gallion.

7
from __future__ import division
8 9 10 11 12
import distutils.command.build_ext
from distutils.dep_util import newer, newer_group
import os
import sys

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
import sip
sip.setapi('QString', 2)

import sipconfig
import PyQt4.QtCore

##################################################################
# try to get various useful things we need in order to build
# this is likely to break, I'm sure

QT_LIB_DIR = PyQt4.QtCore.QLibraryInfo.location(
    PyQt4.QtCore.QLibraryInfo.LibrariesPath)
QT_INC_DIR = PyQt4.QtCore.QLibraryInfo.location(
    PyQt4.QtCore.QLibraryInfo.HeadersPath)
QT_IS_FRAMEWORK = os.path.exists(
    os.path.join(QT_LIB_DIR, 'QtCore.framework') )

try:
    # >= 4.10
    SIP_FLAGS = PyQt4.QtCore.PYQT_CONFIGURATION['sip_flags']
except:
    import PyQt4.pyqtconfig
    SIP_FLAGS = PyQt4.pyqtconfig.Configuration().pyqt_sip_flags

PYQT_SIP_DIR = os.path.join(
    sipconfig.Configuration().default_sip_dir, 'PyQt4')

SIP_BIN = sipconfig.Configuration().sip_bin
SIP_INC_DIR = sipconfig.Configuration().sip_inc_dir

##################################################################
44 45 46 47 48 49 50 51 52 53 54 55 56 57

def replace_suffix(path, new_suffix):
    return os.path.splitext(path)[0] + new_suffix

class build_ext (distutils.command.build_ext.build_ext):

    description = ('Compile SIP descriptions, then build C/C++ extensions '
                   '(compile/link to build directory)')

    def _get_sip_output_list(self, sbf):
        '''
        Parse the sbf file specified to extract the name of the generated source
        files. Make them absolute assuming they reside in the temp directory.
        '''
58
        for L in open(sbf):
59 60 61 62 63 64 65
            key, value = L.split('=', 1)
            if key.strip() == 'sources':
                out = []
                for o in value.split():
                    out.append(os.path.join(self.build_temp, o))
                return out

66
        raise RuntimeError('cannot parse SIP-generated "%s"' % sbf)
67

68
    def get_includes(self):
69 70

        incdirs = []
71
        for mod in ('QtCore', 'QtGui', 'QtXml'):
72 73 74
            if QT_IS_FRAMEWORK:
                incdirs.append(
                    os.path.join(QT_LIB_DIR, mod + '.framework', 'Headers') )
75
            else:
76
                incdirs.append( os.path.join(QT_INC_DIR, mod) )
77 78 79 80 81 82 83 84 85 86 87
        return incdirs

    def swig_sources (self, sources, extension=None):
        if not self.extensions:
            return

        # add directory of input files as include path
        indirs = list(set([os.path.dirname(x) for x in sources]))

        # Add the SIP and Qt include directories to the include path
        extension.include_dirs += [
88 89 90
            SIP_INC_DIR,
            QT_INC_DIR,
            ] + self.get_includes() + indirs
91 92

        # link against libraries
93
        if QT_IS_FRAMEWORK:
94
            extension.extra_link_args = [
95
                '-F', os.path.join(QT_LIB_DIR),
96 97 98 99
                '-framework', 'QtGui',
                '-framework', 'QtCore',
                '-framework', 'QtXml'
                ]
100
        elif sys.platform == 'win32':
101
            extension.libraries = ['QtGui4', 'QtCore4', 'QtXml4']
102
        else:
103
            extension.libraries = ['QtGui', 'QtCore', 'QtXml']
104
        extension.library_dirs = [QT_LIB_DIR]
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130

        depends = extension.depends

        # Filter dependencies list: we are interested only in .sip files,
        # since the main .sip files can only depend on additional .sip
        # files. For instance, if a .h changes, there is no need to
        # run sip again.
        depends = [f for f in depends if os.path.splitext(f)[1] == '.sip']

        # Create the temporary directory if it does not exist already
        if not os.path.isdir(self.build_temp):
            os.makedirs(self.build_temp)

        # Collect the names of the source (.sip) files
        sip_sources = []
        sip_sources = [source for source in sources if source.endswith('.sip')]
        other_sources = [source for source in sources
                         if not source.endswith('.sip')]
        generated_sources = []

        for sip in sip_sources:
            # Use the sbf file as dependency check
            sipbasename = os.path.basename(sip)
            sbf = os.path.join(self.build_temp,
                               replace_suffix(sipbasename, '.sbf'))
            if newer_group([sip]+depends, sbf) or self.force:
131
                self._sip_compile(sip, sbf)
132 133 134 135 136
            out = self._get_sip_output_list(sbf)
            generated_sources.extend(out)

        return generated_sources + other_sources

137 138
    def _sip_compile(self, source, sbf):
        self.spawn([SIP_BIN,
139
                    '-c', self.build_temp,
140 141
                    ] + SIP_FLAGS.split() + [
                    '-I', PYQT_SIP_DIR,
142 143
                    '-b', sbf,
                    source])