initial commit of experimental Phoenix code

git-svn-id: https://svn.wxwidgets.org/svn/wx/sandbox/trunk/Phoenix@66111 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2010-11-11 00:08:20 +00:00
parent 3122bf4f1f
commit ab37739a9b
38 changed files with 18652 additions and 0 deletions

0
buildtools/__init__.py Normal file
View File

503
buildtools/config.py Normal file
View File

@@ -0,0 +1,503 @@
#----------------------------------------------------------------------
# Name: buildtools.config
# Purpose: Code to set and validate platform options and etc. for
# the wxPython build. Moved to their own module and
# class to help setup.py to be simpler.
#
# Author: Robin Dunn
#
# Created: 3-Nov-2010
# Copyright: (c) 2010 by Total Control Software
# Licence: wxWindows license
#----------------------------------------------------------------------
import sys
import os
import glob
import fnmatch
import tempfile
from distutils.file_util import copy_file
from distutils.dir_util import mkpath
from distutils.dep_util import newer
from distutils.spawn import spawn
runSilently = False
#----------------------------------------------------------------------
class Configuration(object):
USE_SIP = True
SIP = '/projects/sip/sip/sipgen/sip'
SIPINC = 'sip/siplib' # Use our local copy of sip.h
SIPGEN = 'sip/gen' # Where the generated .zip files go
SIPFILES = 'sip' # where to find other sip files for %Include or %Import
SIPOUT = 'sip/cpp' # where to put the generated C++ code
SIPOPTS = ' '.join(['-e', # turn on exceptions support
'-k', # turn on keyword args support
'-o', # turn on auto-docstrings
#'-g', # always release and reaquire the GIL
#'-r', # turn on function call tracing
'-I', 'src'
])
WX_CONFIG = None
# Usually you shouldn't need to touch this, but you can set it to
# pass an alternate version of wx-config or alternate flags,
# eg. as required by the .deb in-tree build. By default a
# wx-config command will be assembled based on version, port,
# etc. and it will be looked for on the default $PATH.
WXPORT = 'gtk2'
# On Linux/Unix there are several ports of wxWidgets available.
# Setting this value lets you select which will be used for the
# wxPython build. Possibilites are 'gtk', 'gtk2' and 'x11'.
# Currently only gtk and gtk2 works.
BUILD_BASE = "build"
# Directory to use for temporary build files.
MONOLITHIC = 0
# The core wxWidgets lib can be built as either a single
# monolithic DLL or as a collection of DLLs. This flag controls
# which set of libs will be used on Windows. (For other platforms
# it is automatic via using wx-config.)
WXDLLVER = None
# Version part of wxWidgets LIB/DLL names
COMPILER = 'msvc'
# Used to select which compiler will be used on Windows. This not
# only affects distutils, but also some of the default flags and
# other assumptions in this script. Current supported values are
# 'msvc' and 'mingw32'
ARCH = ''
# If this is set, add an -arch XXX flag to cflags. Only tested (and
# presumably, needed) for OS X.
NO_SCRIPTS = False
# Don't install the tools/script files
PKGDIR = 'wx'
# The name of the top-level package
# ---------------------------------------------------------------
# Basic initialization and configuration code
def __init__(self):
self.CLEANUP = list()
# load the version numbers into this instance's namespace
execfile(opj(os.path.split(__file__)[0], 'version.py'), self.__dict__)
# If we're doing a dated build then alter the VERSION strings
if os.path.exists('DAILY_BUILD'):
self.VER_FLAGS += '.b' + open('DAILY_BUILD').read().strip()
self.VERSION = "%s.%s.%s.%s%s" % (self.VER_MAJOR,
self.VER_MINOR,
self.VER_RELEASE,
self.VER_SUBREL,
self.VER_FLAGS)
self.WXDLLVER = '%d%d' % (self.VER_MAJOR, self.VER_MINOR)
# change the PORT default for wxMac
if sys.platform[:6] == "darwin":
self.WXPORT = 'osx_carbon'
# and do the same for wxMSW, just for consistency
if os.name == 'nt':
self.WXPORT = 'msw'
self.parseCmdLine()
if self.WXPORT != 'msw':
# make sure we only use the compiler value on MSW builds
self.COMPILER=None
self.WXPLAT2 = None
if os.environ.has_key('WXWIN'):
self.WXDIR = os.environ['WXWIN']
else:
if os.path.exists('../wxWidgets'):
self.WXDIR = '../wxWidgets' # assumes in parallel SVN tree
else:
self.WXDIR = '..' # assumes wxPython is subdir
msg("WARNING: WXWIN not set in environment. Assuming '%s'" % self.WXDIR)
self.includes = ['sip/siplib'] # to get our version of sip.h
#---------------------------------------
# MSW specific settings
if os.name == 'nt' and self.COMPILER == 'msvc':
# Set compile flags and such for MSVC. These values are derived
# from the wxWidgets makefiles for MSVC, other compilers settings
# will probably vary...
self.WXPLAT = '__WXMSW__'
if os.environ.get('CPU', None) == 'AMD64':
self.VCDLL = 'vc_amd64_dll'
else:
self.VCDLL = 'vc_dll'
self.includes = ['include', 'src',
opj(WXDIR, 'lib', VCDLL, 'msw' + self.libFlag()),
opj(WXDIR, 'include'),
opj(WXDIR, 'contrib', 'include'),
]
self.defines = [ ('WIN32', None),
('_WINDOWS', None),
(WXPLAT, None),
('WXUSINGDLL', '1'),
('ISOLATION_AWARE_ENABLED', None),
('NDEBUG',), # using a 1-tuple makes it do an undef
]
self.libs = []
self.libdirs = [ opj(self.WXDIR, 'lib', self.VCDLL) ]
if self.MONOLITHIC:
self.libs += makeLibName('')
else:
self.libs += [ 'wxbase' + WXDLLVER + libFlag(),
'wxbase' + WXDLLVER + libFlag() + '_net',
'wxbase' + WXDLLVER + libFlag() + '_xml',
self.makeLibName('core')[0],
self.makeLibName('adv')[0],
self.makeLibName('html')[0],
]
self.libs += ['kernel32', 'user32', 'gdi32', 'comdlg32',
'winspool', 'winmm', 'shell32', 'oldnames', 'comctl32',
'odbc32', 'ole32', 'oleaut32', 'uuid', 'rpcrt4',
'advapi32', 'wsock32']
self.cflags = [ '/Gy',
'/EHsc',
# '/GX-' # workaround for internal compiler error in MSVC on some machines
]
self.lflags = None
# Other MSVC flags...
# Uncomment these to have debug info for all kinds of builds
#self.cflags += ['/Od', '/Z7']
#self.lflags = ['/DEBUG', ]
#---------------------------------------
# Posix (wxGTK, wxMac or mingw32) settings
elif os.name == 'posix' or COMPILER == 'mingw32':
self.Verify_WX_CONFIG()
self.includes = ['include', 'src']
self.defines = [ ('NDEBUG',), # using a 1-tuple makes it do an undef
]
self.libdirs = []
self.libs = []
self.cflags = self.getWxConfigValue('--cxxflags')
self.cflags = self.cflags.split()
if self.debug:
self.cflags.append('-ggdb')
self.cflags.append('-O0')
else:
self.cflags.append('-O3')
lflags = self.getWxConfigValue('--libs')
self.MONOLITHIC = (lflags.find("_xrc") == -1)
self.lflags = lflags.split()
self.WXBASENAME = self.getWxConfigValue('--basename')
self.WXRELEASE = self.getWxConfigValue('--release')
self.WXPREFIX = self.getWxConfigValue('--prefix')
# wxMac settings
if sys.platform[:6] == "darwin":
self.WXPLAT = '__WXMAC__'
if self.WXPORT == 'osx_carbon':
# Flags and such for a Darwin (Max OS X) build of Python
self.WXPLAT2 = '__WXOSX_CARBON__'
else:
self.WXPLAT2 = '__WXOSX_COCOA__'
self.libs = ['stdc++']
if not self.ARCH == "":
self.cflags.append("-arch")
self.cflags.append(self.ARCH)
self.lflags.append("-arch")
self.lflags.append(self.ARCH)
if not os.environ.get('CC') or not os.environ.get('CXX'):
os.environ["CXX"] = self.getWxConfigValue('--cxx')
os.environ["CC"] = self.getWxConfigValue('--cc')
# wxGTK settings
else:
# Set flags for other Unix type platforms
if self.WXPORT == 'gtk':
msg("WARNING: The GTK 1.x port is not supported")
self.WXPLAT = '__WXGTK__'
portcfg = os.popen('gtk-config --cflags', 'r').read()[:-1]
self.BUILD_BASE = self.BUILD_BASE + '-' + self.WXPORT
elif self.WXPORT == 'gtk2':
self.WXPLAT = '__WXGTK__'
portcfg = os.popen('pkg-config gtk+-2.0 --cflags', 'r').read()[:-1]
elif self.WXPORT == 'x11':
msg("WARNING: The wxX11 port is no supported")
self.WXPLAT = '__WXX11__'
portcfg = ''
self.BUILD_BASE = self.BUILD_BASE + '-' + self.WXPORT
elif self.WXPORT == 'msw':
self.WXPLAT = '__WXMSW__'
portcfg = ''
else:
raise SystemExit, "Unknown WXPORT value: " + self.WXPORT
self.cflags += portcfg.split()
# Some distros (e.g. Mandrake) put libGLU in /usr/X11R6/lib, but
# wx-config doesn't output that for some reason. For now, just
# add it unconditionally but we should really check if the lib is
# really found there or wx-config should be fixed.
if self.WXPORT != 'msw':
self.libdirs.append("/usr/X11R6/lib")
# Move the various -I, -D, etc. flags we got from the config scripts
# into the distutils lists.
self.cflags = self.adjustCFLAGS(self.cflags, self.defines, self.includes)
self.lflags = self.adjustLFLAGS(self.lflags, self.libdirs, self.libs)
if self.debug and self.WXPORT == 'msw' and self.COMPILER != 'mingw32':
self.defines.append( ('_DEBUG', None) )
# ---------------------------------------------------------------
# Helper functions
def parseCmdLine(self):
self.debug = '--debug' in sys.argv or '-g' in sys.argv
# the values of the items in the class namespace that start
# with an upper case letter can be overridden on the command
# line
for key, default in Configuration.__dict__.items():
if key[0] < 'A' or key[0] > 'Z':
continue
for idx, arg in enumerate(sys.argv):
if arg and arg.startswith(key + '='):
value = arg.split('=', 1)[1]
if isinstance(default, int):
value = int(value)
setattr(self, key, value)
sys.argv[idx] = None
# remove the cmd line args that we recognized
sys.argv = [arg for arg in sys.argv if arg is not None]
def Verify_WX_CONFIG(self):
"""
Called for the builds that need wx-config. If WX_CONFIG is
not set then determines the flags needed based on build
options and searches for wx-config on the PATH.
"""
# if WX_CONFIG hasn't been set to an explicit value then construct one.
if self.WX_CONFIG is None:
self.WX_CONFIG='wx-config'
port = self.WXPORT
if port == "x11":
port = "x11univ"
flags = ' --toolkit=%s' % port
flags += ' --unicode=yes'
flags += ' --version=%s.%s' % (self.VER_MAJOR, self.VER_MINOR)
searchpath = os.environ["PATH"]
for p in searchpath.split(':'):
fp = os.path.join(p, 'wx-config')
if os.path.exists(fp) and os.access(fp, os.X_OK):
# success
msg("Found wx-config: " + fp)
msg(" Using flags: " + flags)
self.WX_CONFIG = fp + flags
if runSilently:
self.WX_CONFIG += " 2>/dev/null "
break
else:
msg("ERROR: WX_CONFIG not specified and wx-config not found on the $PATH")
# TODO: execute WX_CONFIG --list and verify a matching config is found
def getWxConfigValue(self, flag):
cmd = "%s %s" % (self.WX_CONFIG, flag)
value = os.popen(cmd, 'r').read()[:-1]
return value
def build_locale_dir(self, destdir, verbose=1):
"""Build a locale dir under the wxPython package for MSW"""
moFiles = glob.glob(opj(self.WXDIR, 'locale', '*.mo'))
for src in moFiles:
lang = os.path.splitext(os.path.basename(src))[0]
dest = opj(destdir, lang, 'LC_MESSAGES')
mkpath(dest, verbose=verbose)
copy_file(src, opj(dest, 'wxstd.mo'), update=1, verbose=verbose)
self.CLEANUP.append(opj(dest, 'wxstd.mo'))
self.CLEANUP.append(dest)
def build_locale_list(self, srcdir):
# get a list of all files under the srcdir, to be used for install_data
def walk_helper(lst, dirname, files):
for f in files:
filename = opj(dirname, f)
if not os.path.isdir(filename):
lst.append( (dirname, [filename]) )
file_list = []
os.path.walk(srcdir, walk_helper, file_list)
return file_list
def find_data_files(self, srcdir, *wildcards, **kw):
# get a list of all files under the srcdir matching wildcards,
# returned in a format to be used for install_data
def walk_helper(arg, dirname, files):
if '.svn' in dirname:
return
names = []
lst, wildcards = arg
for wc in wildcards:
wc_name = opj(dirname, wc)
for f in files:
filename = opj(dirname, f)
if fnmatch.fnmatch(filename, wc_name) and not os.path.isdir(filename):
names.append(filename)
if names:
lst.append( (dirname, names ) )
file_list = []
recursive = kw.get('recursive', True)
if recursive:
os.path.walk(srcdir, walk_helper, (file_list, wildcards))
else:
walk_helper((file_list, wildcards),
srcdir,
[os.path.basename(f) for f in glob.glob(opj(srcdir, '*'))])
return file_list
def makeLibName(self, name):
if os.name == 'posix' or self.COMPILER == 'mingw32':
libname = '%s_%s-%s' % (self.WXBASENAME, name, self.WXRELEASE)
elif name:
libname = 'wxmsw%s%s_%s' % (self.WXDLLVER, self.libFlag(), name)
else:
libname = 'wxmsw%s%s' % (self.WXDLLVER, self.libFlag())
return [libname]
def libFlag(self):
if not self.debug:
rv = ''
else:
rv = 'd'
if True: ##UNICODE:
rv = 'u' + rv
return rv
def findLib(self, name, libdirs):
name = self.makeLibName(name)[0]
if os.name == 'posix' or self.COMPILER == 'mingw32':
lflags = self.getWxConfigValue('--libs')
lflags = lflags.split()
# if wx-config --libs output does not start with -L, wx is
# installed with a standard prefix and wx-config does not
# output these libdirs because they are already searched by
# default by the compiler and linker.
if lflags[0][:2] != '-L':
dirs = libdirs + ['/usr/lib', '/usr/local/lib']
else:
dirs = libdirs
name = 'lib'+name
else:
dirs = libdirs[:]
for d in dirs:
p = os.path.join(d, name)
if glob.glob(p+'*') != []:
return True
return False
def adjustCFLAGS(self, cflags, defines, includes):
"""
Extract the raw -I, -D, and -U flags from cflags and put them into
defines and includes as needed.
"""
newCFLAGS = []
for flag in cflags:
if flag[:2] == '-I':
includes.append(flag[2:])
elif flag[:2] == '-D':
flag = flag[2:]
if flag.find('=') == -1:
defines.append( (flag, None) )
else:
defines.append( tuple(flag.split('=')) )
elif flag[:2] == '-U':
defines.append( (flag[2:], ) )
else:
newCFLAGS.append(flag)
return newCFLAGS
def adjustLFLAGS(self, lflags, libdirs, libs):
"""
Extract the -L and -l flags from lflags and put them in libdirs and
libs as needed
"""
newLFLAGS = []
for flag in lflags:
if flag[:2] == '-L':
libdirs.append(flag[2:])
elif flag[:2] == '-l':
libs.append(flag[2:])
else:
newLFLAGS.append(flag)
return newLFLAGS
# We'll use a factory function so we can use the Configuration class as a singleton
_config = None
def Config(*args, **kw):
global _config
if _config is None:
_config = Configuration(*args, **kw)
return _config
#----------------------------------------------------------------------
# other helpers
def msg(text):
if not runSilently:
print text
def opj(*args):
path = os.path.join(*args)
return os.path.normpath(path)

View File

@@ -0,0 +1,375 @@
#----------------------------------------------------------------------
# Name: buildtools.distutils_hacks
# Purpose: Various hacks that have been needed to override features
# or work-around problems in Python's distutils.
#
# Author: Robin Dunn
#
# Created: 3-Nov-2010
# Copyright: (c) 2010 by Total Control Software
# Licence: wxWindows license
#----------------------------------------------------------------------
import sys
import os
import distutils.command.install
import distutils.command.install_data
import distutils.command.install_headers
import distutils.command.clean
from distutils.dep_util import newer
from config import Config
#----------------------------------------------------------------------
# New command classes
class wx_smart_install_data(distutils.command.install_data.install_data):
"""need to change self.install_dir to the actual library dir"""
def run(self):
install_cmd = self.get_finalized_command('install')
self.install_dir = getattr(install_cmd, 'install_lib')
return distutils.command.install_data.install_data.run(self)
class wx_extra_clean(distutils.command.clean.clean):
"""
Also cleans stuff that this setup.py copies itself. If the
--all flag was used also searches for .pyc, .pyd, .so files
"""
def run(self):
from distutils import log
from distutils.filelist import FileList
distutils.command.clean.clean.run(self)
cfg = Config()
if self.all:
fl = FileList()
fl.include_pattern("*.pyc", 0)
fl.include_pattern("*.pyd", 0)
fl.include_pattern("*.so", 0)
cfg.CLEANUP += fl.files
for f in cfg.CLEANUP:
if os.path.isdir(f):
try:
if not self.dry_run and os.path.exists(f):
os.rmdir(f)
log.info("removing '%s'", f)
except IOError:
log.warning("unable to remove '%s'", f)
else:
try:
if not self.dry_run and os.path.exists(f):
os.remove(f)
log.info("removing '%s'", f)
except IOError:
log.warning("unable to remove '%s'", f)
# The Ubuntu Python adds a --install-layout option to distutils that
# is used in our package build. If we detect that the current
# distutils does not have it then make sure that it is removed from
# the command-line options, otherwise the build will fail.
for item in distutils.command.install.install.user_options:
if item[0] == 'install-layout=':
break
else:
for arg in sys.argv:
if arg.startswith('--install-layout'):
sys.argv.remove(arg)
break
class wx_install(distutils.command.install.install):
"""
Turns off install_path_file
"""
def initialize_options(self):
distutils.command.install.install.initialize_options(self)
self.install_path_file = 0
class wx_install_headers(distutils.command.install_headers.install_headers):
"""
Install the header files to the WXPREFIX, with an extra dir per
filename too
"""
def initialize_options(self):
self.root = None
distutils.command.install_headers.install_headers.initialize_options(self)
def finalize_options(self):
self.set_undefined_options('install', ('root', 'root'))
distutils.command.install_headers.install_headers.finalize_options(self)
def run(self):
if os.name == 'nt':
return
headers = self.distribution.headers
if not headers:
return
cfg = Config()
root = self.root
#print "WXPREFIX is %s, root is %s" % (WXPREFIX, root)
# hack for universal builds, which append i386/ppc
# to the root
if root is None or cfg.WXPREFIX.startswith(os.path.dirname(root)):
root = ''
for header, location in headers:
install_dir = os.path.normpath(root +
cfg.WXPREFIX +
'/include/wx-%d.%d/wx' % (cfg.VER_MAJOR, cfg.VER_MINOR) +
location)
self.mkpath(install_dir)
(out, _) = self.copy_file(header, install_dir)
self.outfiles.append(out)
#----------------------------------------------------------------------
# These functions and class are copied from distutils in Python 2.5
# and then grafted back into the distutils modules so we can change
# how the -arch and -isysroot compiler args are handled. Basically if
# -arch is specified in our compiler args then we need to strip all of
# the -arch and -isysroot args provided by Python.
import distutils.unixccompiler
import distutils.sysconfig
from distutils.errors import DistutilsExecError, CompileError
def _darwin_compiler_fixup(compiler_so, cc_args):
"""
This function will strip '-isysroot PATH' and '-arch ARCH' from the
compile flags if the user has specified one them in extra_compile_flags.
This is needed because '-arch ARCH' adds another architecture to the
build, without a way to remove an architecture. Furthermore GCC will
barf if multiple '-isysroot' arguments are present.
"""
stripArch = stripSysroot = 0
compiler_so = list(compiler_so)
kernel_version = os.uname()[2] # 8.4.3
major_version = int(kernel_version.split('.')[0])
if major_version < 8:
# OSX before 10.4.0, these don't support -arch and -isysroot at
# all.
stripArch = stripSysroot = True
else:
stripArch = '-arch' in cc_args
stripSysroot = '-isysroot' in cc_args or stripArch # <== This line changed
if stripArch:
while 1:
try:
index = compiler_so.index('-arch')
# Strip this argument and the next one:
del compiler_so[index:index+2]
except ValueError:
break
if stripSysroot:
try:
index = compiler_so.index('-isysroot')
# Strip this argument and the next one:
del compiler_so[index:index+2]
except ValueError:
pass
# Check if the SDK that is used during compilation actually exists,
# the universal build requires the usage of a universal SDK and not all
# users have that installed by default.
sysroot = None
if '-isysroot' in cc_args:
idx = cc_args.index('-isysroot')
sysroot = cc_args[idx+1]
elif '-isysroot' in compiler_so:
idx = compiler_so.index('-isysroot')
sysroot = compiler_so[idx+1]
if sysroot and not os.path.isdir(sysroot):
log.warn("Compiling with an SDK that doesn't seem to exist: %s",
sysroot)
log.warn("Please check your Xcode installation")
return compiler_so
def _darwin_compiler_fixup_24(compiler_so, cc_args):
compiler_so = _darwin_compiler_fixup(compiler_so, cc_args)
return compiler_so, cc_args
class MyUnixCCompiler(distutils.unixccompiler.UnixCCompiler):
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
compiler_so = self.compiler_so
if sys.platform == 'darwin':
compiler_so = _darwin_compiler_fixup(compiler_so, cc_args + extra_postargs)
try:
self.spawn(compiler_so + cc_args + [src, '-o', obj] +
extra_postargs)
except DistutilsExecError, msg:
raise CompileError, msg
_orig_parse_makefile = distutils.sysconfig.parse_makefile
def _parse_makefile(filename, g=None):
rv = _orig_parse_makefile(filename, g)
# If a different deployment target is specified in the
# environment then make sure it is put in the global
# config dict.
if os.getenv('MACOSX_DEPLOYMENT_TARGET'):
val = os.getenv('MACOSX_DEPLOYMENT_TARGET')
rv['MACOSX_DEPLOYMENT_TARGET'] = val
rv['CONFIGURE_MACOSX_DEPLOYMENT_TARGET'] = val
return rv
distutils.unixccompiler.UnixCCompiler = MyUnixCCompiler
distutils.unixccompiler._darwin_compiler_fixup = _darwin_compiler_fixup
distutils.unixccompiler._darwin_compiler = _darwin_compiler_fixup_24
distutils.sysconfig.parse_makefile = _parse_makefile
#----------------------------------------------------------------------
# Another hack-job for the CygwinCCompiler class, this time replacing
# the _compile function with one that will pass the -I flags to windres.
import distutils.cygwinccompiler
from distutils.errors import DistutilsExecError, CompileError
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
if ext == '.rc' or ext == '.res':
# gcc needs '.res' and '.rc' compiled to object files !!!
try:
#self.spawn(["windres", "-i", src, "-o", obj])
self.spawn(["windres", "-i", src, "-o", obj] +
[arg for arg in cc_args if arg.startswith("-I")] )
except DistutilsExecError, msg:
raise CompileError, msg
else: # for other files use the C-compiler
try:
self.spawn(self.compiler_so + cc_args + [src, '-o', obj] +
extra_postargs)
except DistutilsExecError, msg:
raise CompileError, msg
distutils.cygwinccompiler.CygwinCCompiler._compile = _compile
#----------------------------------------------------------------------
# Yet another distutils hack, this time for the msvc9compiler. There
# is a bug in at least version distributed with Python 2.6 where it
# adds '/pdb:None' to the linker command-line, but that just results
# in a 'None' file being created instead of putting the debug info
# into the .pyd files as expected. So we'll strip out that option via
# a monkey-patch of the msvc9compiler.MSVCCompiler.initialize method.
if os.name == 'nt' and sys.version_info >= (2,6):
import distutils.msvc9compiler
_orig_initialize = distutils.msvc9compiler.MSVCCompiler.initialize
def _initialize(self, *args, **kw):
rv = _orig_initialize(self, *args, **kw)
try:
self.ldflags_shared_debug.remove('/pdb:None')
except ValueError:
pass
return rv
distutils.msvc9compiler.MSVCCompiler.initialize = _initialize
#----------------------------------------------------------------------
from sipdistutils import build_ext
class etgsip_build_ext(build_ext):
"""
Override some parts of the SIP build command class so we can better
control how SIP is used
"""
def _find_sip(self):
cfg = Config()
return cfg.SIP
def _sip_inc_dir(self):
cfg = Config()
return cfg.SIPINC
def _sip_sipfiles_dir(self):
cfg = Config()
return cfg.SIPFILES
def _sip_output_dir(self):
cfg = Config()
return cfg.SIPOUT
def etg2sip(self, etgfile):
cfg = Config()
sipfile = os.path.splitext(os.path.basename(etgfile))[0] + '.sip'
sipfile = os.path.join(cfg.SIPGEN, sipfile)
return sipfile
def build_extension(self, extension):
"""
Modify the dependency list, adding the sip files generated
from the etg files.
"""
sources = extension.sources
if sources is not None and isinstance(sources, (list, tuple)):
etg_sources = [s for s in sources if s.startswith('etg/')]
for e in etg_sources:
extension.depends.append(self.etg2sip(e))
# let the base class do the rest
return build_ext.build_extension(self, extension)
def swig_sources (self, sources, extension):
"""
Run our ETG scripts to generate their .sip files, and adjust
the sources list before passing on to the base class, which
will then be responsible for running SIP and building the
generated C++ files.
"""
if not self.extensions:
return
cfg = Config()
etg_sources = [s for s in sources if s.startswith('etg/')]
other_sources = [s for s in sources if not s.startswith('etg/')]
for etg in etg_sources:
sipfile = self.etg2sip(etg)
if newer(etg, sipfile):
cmd = [sys.executable, etg, '--sip']
#if cfg.verbose:
# cmd.append('--verbose')
self.spawn(cmd)
if '%Module ' in file(sipfile).read():
other_sources.append(sipfile)
# now call the base class version of this method
return build_ext.swig_sources(self, other_sources, extension)

155
buildtools/sipdistutils.py Normal file
View File

@@ -0,0 +1,155 @@
# 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.
# NOTE: This has been tweaked slightly to allow the folder used for the SIP
# output to be overridden in a derived class. It is otherwise the same as the
# module provided by sip. --Robin
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 = "Compile 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._sip_output_dir(), 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._sip_output_dir(), "sip.signature")
def _sip_output_dir(self):
return self.build_temp
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._sip_output_dir()):
os.makedirs(self._sip_output_dir())
# 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._sip_output_dir(), 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._sip_output_dir(),
"-b", sbf,
"-I", self._sip_sipfiles_dir(),
source])

8
buildtools/version.py Normal file
View File

@@ -0,0 +1,8 @@
# wxPython version numbers used in build
VER_MAJOR = 2 # The first three must match wxWidgets
VER_MINOR = 9
VER_RELEASE = 2
VER_SUBREL = 0 # wxPython release num for x.y.z release of wxWidgets
VER_FLAGS = "" # release flags, such as prerelease or RC num, etc.