You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
5.1 KiB

# 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):
self.sip_opts = None
def finalize_options (self):
if self.sip_opts is None:
self.sip_opts = []
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 == "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
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:
# Add the SIP include directory to the include path
if extension is not None:
depends = extension.depends
# pre-2.4 compatibility
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):
# 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)
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(),