Hi Phil,

please find attached an improved sipdistutils.py, and the diff against
current version for reference.

Modifications:

 * Added command line option to pass arguments to sip. Now you can for
instance do: "python setup.py build --sip-opts='-e'" to activate
exceptions. Or you can put the "sip-opts='-e'" in a local distutils.cfg
file in your source directory.

 * Fixed invocation of sip.exe under Windows. I still maintain that
siputils.py should put the full pathname of sip, including extension,
under the "sip_bin" in the configuration, but a quick workaround is
needed anyway to make sipdistutils work with all versions of sip.

 * Consider the sip binary checksum as an implicit dependency for build.
This is very useful when switching between different sip versions,
because everything is automatically recompiled.

Coming up:

 * As discussed some months ago, I plan to add an option to sip itself
to printf() any .sip file it opens. I will then use a dry-run with this
option to automatically discover dependencies so to rebuild whenever
necessary. This would be a strong improvement over the current state
where one must manually list all sip files (theoretically, of all
imported modules as well) in the setup.py.

-- 
Giovanni Bajo
Develer S.r.l.
http://www.develer.com

--- /tmp/sipdistutils.py	2009-11-09 04:30:17.000000000 +0100
+++ sipdistutils.py	2009-10-28 19:02:17.000000000 +0100
@@ -8,14 +8,35 @@
 from distutils.dep_util import newer, newer_group
 import os
 import sys
+from hashlib import sha1
+
+build_ext_base = distutils.command.build_ext.build_ext
 
 def replace_suffix(path, new_suffix):
     return os.path.splitext(path)[0] + new_suffix
 
-class build_ext (distutils.command.build_ext.build_ext):
+class build_ext (build_ext_base):
 
     description = "Compiler SIP descriptions, then build C/C++ extensions (compile/link to build directory)"
 
+    user_options = build_ext_base.user_options[:]
+    user_options = [opt for opt in user_options if not opt[0].startswith("swig")]
+    user_options += [
+        ('sip-opts=', None,
+         "list of sip command line options"),
+    ]
+
+    def initialize_options (self):
+        build_ext_base.initialize_options(self)
+        self.sip_opts = None
+
+    def finalize_options (self):
+        build_ext_base.finalize_options(self)
+        if self.sip_opts is None:
+            self.sip_opts = []
+        else:
+            self.sip_opts = self.sip_opts.split(' ')
+
     def _get_sip_output_list(self, sbf):
         """
         Parse the sbf file specified to extract the name of the generated source
@@ -34,6 +55,9 @@
     def _find_sip(self):
         import sipconfig
         cfg = sipconfig.Configuration()
+        if os.name == "nt":
+            if not os.path.splitext(os.path.basename(cfg.sip_bin))[1]:
+                return cfg.sip_bin + ".exe"
         return cfg.sip_bin
 
     def _sip_inc_dir(self):
@@ -41,6 +65,37 @@
         cfg = sipconfig.Configuration()
         return cfg.sip_inc_dir
 
+    def _sip_sipfiles_dir(self):
+        import sipconfig
+        cfg = sipconfig.Configuration()
+        return cfg.default_sip_dir
+
+    def _sip_calc_signature(self):
+        sip_bin = self._find_sip()
+        return sha1(open(sip_bin, "rb").read()).hexdigest()
+
+    def _sip_signature_file(self):
+        return os.path.join(self.build_temp, "sip.signature")
+
+    def build_extension (self, ext):
+        oldforce = self.force
+
+        if not self.force:
+            sip_sources = [source for source in ext.sources if source.endswith('.sip')]
+            if sip_sources:
+                sigfile = self._sip_signature_file()
+                if not os.path.isfile(sigfile):
+                    self.force = True
+                else:
+                    old_sig = open(sigfile).read()
+                    new_sig = self._sip_calc_signature()
+                    if old_sig != new_sig:
+                        self.force = True
+
+        build_ext_base.build_extension(self, ext)
+
+        self.force = oldforce
+
     def swig_sources (self, sources, extension=None):
         if not self.extensions:
             return
@@ -78,13 +133,16 @@
             sbf = os.path.join(self.build_temp, replace_suffix(sipbasename, ".sbf"))
             if newer_group([sip]+depends, sbf) or self.force:
                 self._sip_compile(sip_bin, sip, sbf)
+                open(self._sip_signature_file(), "w").write(self._sip_calc_signature())
             out = self._get_sip_output_list(sbf)
             generated_sources.extend(out)
 
         return generated_sources + other_sources
 
     def _sip_compile(self, sip_bin, source, sbf):
-        self.spawn([sip_bin,
-                    "-c", self.build_temp,
+        self.spawn([sip_bin] + self.sip_opts +
+                    ["-c", self.build_temp,
                     "-b", sbf,
+                    "-I", self._sip_sipfiles_dir(),
                     source])
+
# 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.

import distutils.command.build_ext
from distutils.dep_util import newer, newer_group
import os
import sys
from hashlib import sha1

build_ext_base = distutils.command.build_ext.build_ext

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

class build_ext (build_ext_base):

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

    user_options = build_ext_base.user_options[:]
    user_options = [opt for opt in user_options if not opt[0].startswith("swig")]
    user_options += [
        ('sip-opts=', None,
         "list of sip command line options"),
    ]

    def initialize_options (self):
        build_ext_base.initialize_options(self)
        self.sip_opts = None

    def finalize_options (self):
        build_ext_base.finalize_options(self)
        if self.sip_opts is None:
            self.sip_opts = []
        else:
            self.sip_opts = self.sip_opts.split(' ')

    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.
        """
        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(self.build_temp, o))
                return out

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

    def _find_sip(self):
        import sipconfig
        cfg = sipconfig.Configuration()
        if os.name == "nt":
            if not os.path.splitext(os.path.basename(cfg.sip_bin))[1]:
                return cfg.sip_bin + ".exe"
        return cfg.sip_bin

    def _sip_inc_dir(self):
        import sipconfig
        cfg = sipconfig.Configuration()
        return cfg.sip_inc_dir

    def _sip_sipfiles_dir(self):
        import sipconfig
        cfg = sipconfig.Configuration()
        return cfg.default_sip_dir

    def _sip_calc_signature(self):
        sip_bin = self._find_sip()
        return sha1(open(sip_bin, "rb").read()).hexdigest()

    def _sip_signature_file(self):
        return os.path.join(self.build_temp, "sip.signature")

    def build_extension (self, ext):
        oldforce = self.force

        if not self.force:
            sip_sources = [source for source in ext.sources if source.endswith('.sip')]
            if sip_sources:
                sigfile = self._sip_signature_file()
                if not os.path.isfile(sigfile):
                    self.force = True
                else:
                    old_sig = open(sigfile).read()
                    new_sig = self._sip_calc_signature()
                    if old_sig != new_sig:
                        self.force = True

        build_ext_base.build_extension(self, ext)

        self.force = oldforce

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

        # Add the SIP include directory to the include path
        if extension is not None:
            extension.include_dirs.append(self._sip_inc_dir())
            depends = extension.depends
        else:
            # pre-2.4 compatibility
            self.include_dirs.append(self._sip_inc_dir())
            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 = []

        sip_bin = self._find_sip()

        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:
                self._sip_compile(sip_bin, sip, sbf)
                open(self._sip_signature_file(), "w").write(self._sip_calc_signature())
            out = self._get_sip_output_list(sbf)
            generated_sources.extend(out)

        return generated_sources + other_sources

    def _sip_compile(self, sip_bin, source, sbf):
        self.spawn([sip_bin] + self.sip_opts +
                    ["-c", self.build_temp,
                    "-b", sbf,
                    "-I", self._sip_sipfiles_dir(),
                    source])

_______________________________________________
PyQt mailing list    [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

Reply via email to