mirror of
https://github.com/wxWidgets/Phoenix.git
synced 2026-01-04 19:10:09 +01:00
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:
1
README.txt
Normal file
1
README.txt
Normal file
@@ -0,0 +1 @@
|
||||
See http://wiki.wxpython.org/ProjectPhoenix for details about this project.
|
||||
0
buildtools/__init__.py
Normal file
0
buildtools/__init__.py
Normal file
503
buildtools/config.py
Normal file
503
buildtools/config.py
Normal 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)
|
||||
375
buildtools/distutils_hacks.py
Normal file
375
buildtools/distutils_hacks.py
Normal 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
155
buildtools/sipdistutils.py
Normal 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
8
buildtools/version.py
Normal 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.
|
||||
3
etg/README.txt
Normal file
3
etg/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
This directory contains Extractror-Tweaker-Generator (ETG) scripts which are
|
||||
used to drive the process of converting the wxWidgets Doxygen XML files into
|
||||
the files that will be fed to the bindings generator tool (SIP).
|
||||
82
etg/_core.py
Normal file
82
etg/_core.py
Normal file
@@ -0,0 +1,82 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: _core.py
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 8-Nove-2010
|
||||
# RCS-ID: $Id:$
|
||||
# Copyright: (c) 2010 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
PACKAGE = "wx"
|
||||
MODULE = "_core"
|
||||
NAME = "_core" # Base name of the file to generate to for this script
|
||||
DOCSTRING = ""
|
||||
|
||||
# The classes and/or the basename of the Doxygen XML files to be processed by
|
||||
# this script.
|
||||
ITEMS = [
|
||||
'defs_8h.xml'
|
||||
]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Parse the XML file(s) building a collection of Extractor objects
|
||||
|
||||
import etgtools
|
||||
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
|
||||
etgtools.parseDoxyXML(module, ITEMS)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Tweak the parsed meta objects in the module object as needed for customizing
|
||||
# the generated code and docstrings.
|
||||
|
||||
import etgtools.tweaker_tools
|
||||
etgtools.tweaker_tools.ignoreAssignmentOperators(module)
|
||||
etgtools.tweaker_tools.removeWxPrefixes(module)
|
||||
|
||||
|
||||
# These items are in their own etg scripts for easier maintainability,
|
||||
# but their class and function definitions are intended to be part of
|
||||
# this module, not their own module. This also makes it easier to
|
||||
# promote one of these to module status later if desired, simply
|
||||
# remove it fron this list of Includes, and change the MODULE value in
|
||||
# the promoted script to be the same as its NAME.
|
||||
|
||||
module.addInclude(['string',
|
||||
'object',
|
||||
'gdicmn',
|
||||
'geometry',
|
||||
])
|
||||
|
||||
|
||||
# tweaks for defs.h
|
||||
module.find('wxInt16').type = 'short'
|
||||
module.find('wxInt64').type = 'long long'
|
||||
module.find('wxUint64').type = 'unsigned long long'
|
||||
module.find('wxIntPtr').type = 'long' #'ssize_t'
|
||||
module.find('wxUIntPtr').type = 'unsigned long' #'size_t'
|
||||
|
||||
module.find('wxDELETE').ignore()
|
||||
module.find('wxDELETEA').ignore()
|
||||
module.find('wxSwap').ignore()
|
||||
module.find('wxVaCopy').ignore()
|
||||
|
||||
# add some typedefs for wxChar, wxUChar
|
||||
td = module.find('wxUIntPtr')
|
||||
module.insertItemAfter(td, etgtools.TypedefDef(type='wchar_t', name='wxUChar'))
|
||||
module.insertItemAfter(td, etgtools.TypedefDef(type='wchar_t', name='wxChar'))
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
#---------------------------------------------------------------------------
|
||||
# Run the generators
|
||||
|
||||
# Create the code generator and make the wrapper code
|
||||
wg = etgtools.getWrapperGenerator()
|
||||
wg.generate(module)
|
||||
|
||||
# Create a documentation generator and let it do its thing
|
||||
dg = etgtools.getDocsGenerator()
|
||||
dg.generate(module)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
228
etg/gdicmn.py
Normal file
228
etg/gdicmn.py
Normal file
@@ -0,0 +1,228 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: gdicmn.py
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 4-Nov-2010
|
||||
# RCS-ID: $Id:$
|
||||
# Copyright: (c) 2010 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
PACKAGE = "wx"
|
||||
MODULE = "_core"
|
||||
NAME = "gdicmn"
|
||||
DOCSTRING = ""
|
||||
|
||||
# The classes and/or the basename of the Doxygen XML files to be processed by
|
||||
# this script.
|
||||
ITEMS = [
|
||||
'wxPoint',
|
||||
'wxSize',
|
||||
'wxRect',
|
||||
'wxRealPoint',
|
||||
]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Parse the XML file(s) building a collection of Extractor objects
|
||||
|
||||
import etgtools
|
||||
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
|
||||
etgtools.parseDoxyXML(module, ITEMS)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Tweak the parsed meta objects in the module object as needed for customizing
|
||||
# the generated code and docstrings.
|
||||
|
||||
import etgtools.tweaker_tools
|
||||
etgtools.tweaker_tools.ignoreAssignmentOperators(module)
|
||||
etgtools.tweaker_tools.removeWxPrefixes(module)
|
||||
|
||||
|
||||
module.addHeaderCode('#include <wx/wx.h>')
|
||||
|
||||
# ignore some of these enum values
|
||||
e = module.find('wxBitmapType')
|
||||
for i in e:
|
||||
if i.name.endswith('_RESOURCE'):
|
||||
i.ignore()
|
||||
|
||||
# TODO: We need an ETG way to indicate that an item is only available
|
||||
# on certain platofmrs.
|
||||
e = module.find('wxStockCursor')
|
||||
e.find('wxCURSOR_BASED_ARROW_DOWN').ignore()
|
||||
e.find('wxCURSOR_BASED_ARROW_UP').ignore()
|
||||
e.find('wxCURSOR_CROSS_REVERSE').ignore()
|
||||
e.find('wxCURSOR_DOUBLE_ARROW').ignore()
|
||||
|
||||
|
||||
module.find('wxTheColourDatabase').ignore() # TODO
|
||||
module.find('wxSetCursor').ignore() # TODO
|
||||
|
||||
module.find('wxClientDisplayRect.x').out = True
|
||||
module.find('wxClientDisplayRect.y').out = True
|
||||
module.find('wxClientDisplayRect.width').out = True
|
||||
module.find('wxClientDisplayRect.height').out = True
|
||||
|
||||
module.find('wxDisplaySize.width').out = True
|
||||
module.find('wxDisplaySize.height').out = True
|
||||
module.find('wxDisplaySizeMM.width').out = True
|
||||
module.find('wxDisplaySizeMM.height').out = True
|
||||
|
||||
#---------------------------------------
|
||||
# wxPoint tweaks
|
||||
c = module.find('wxPoint')
|
||||
|
||||
# Some operators are documented within the class that shouldn't be, so just
|
||||
# ignore them all.
|
||||
etgtools.tweaker_tools.ignoreAllOperators(c)
|
||||
|
||||
# Undo a few of those ignores for legitimate items that were
|
||||
# documented correctly
|
||||
for f in c.find('operator+=').all() + c.find('operator-=').all():
|
||||
f.ignore(False)
|
||||
|
||||
# Add some stand-alone function declarations for the operators that really do
|
||||
# exist.
|
||||
wc = etgtools.WigCode("""\
|
||||
bool operator==(const wxPoint& p1, const wxPoint& p2);
|
||||
bool operator!=(const wxPoint& p1, const wxPoint& p2);
|
||||
wxPoint operator+(const wxPoint& p, const wxSize& s);
|
||||
wxPoint operator+(const wxPoint& p1, const wxPoint& p2);
|
||||
wxPoint operator+(const wxSize& s, const wxPoint& p);
|
||||
wxPoint operator-(const wxPoint& p);
|
||||
wxPoint operator-(const wxPoint& p, const wxSize& s);
|
||||
wxPoint operator-(const wxPoint& p1, const wxPoint& p2);
|
||||
wxPoint operator-(const wxSize& s, const wxPoint& p);
|
||||
wxPoint operator*(const wxPoint& s, int i);
|
||||
wxPoint operator*(int i, const wxPoint& s);
|
||||
wxPoint operator/(const wxPoint& s, int i);
|
||||
""")
|
||||
module.insertItemAfter(c, wc)
|
||||
|
||||
# wxPoint typemap
|
||||
c.convertFromPyObject = """\
|
||||
// is it just a typecheck?
|
||||
if (!sipIsErr) {
|
||||
if (sipCanConvertToType(sipPy, sipType_wxPoint, SIP_NO_CONVERTORS))
|
||||
return 1;
|
||||
|
||||
if (PySequence_Check(sipPy) and PySequence_Size(sipPy) == 2) {
|
||||
int rval = 1;
|
||||
PyObject* o1 = PySequence_ITEM(sipPy, 0);
|
||||
PyObject* o2 = PySequence_ITEM(sipPy, 1);
|
||||
if (!PyNumber_Check(o1) || !PyNumber_Check(o2))
|
||||
rval = 0;
|
||||
Py_DECREF(o1);
|
||||
Py_DECREF(o2);
|
||||
return rval;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// otherwise do the conversion
|
||||
if (sipPy == Py_None) {
|
||||
*sipCppPtr = new wxPoint(-1, -1);
|
||||
return sipGetState(sipTransferObj);
|
||||
}
|
||||
|
||||
if (PySequence_Check(sipPy)) {
|
||||
PyObject* o1 = PySequence_ITEM(sipPy, 0);
|
||||
PyObject* o2 = PySequence_ITEM(sipPy, 1);
|
||||
*sipCppPtr = new wxPoint(PyInt_AsLong(o1), PyInt_AsLong(o2));
|
||||
Py_DECREF(o1);
|
||||
Py_DECREF(o2);
|
||||
return sipGetState(sipTransferObj);
|
||||
}
|
||||
|
||||
*sipCppPtr = reinterpret_cast<wxPoint*>(sipConvertToType(
|
||||
sipPy, sipType_wxPoint, sipTransferObj, SIP_NO_CONVERTORS, 0, sipIsErr));
|
||||
return 0;
|
||||
"""
|
||||
|
||||
|
||||
#---------------------------------------
|
||||
# wxSize tweaks
|
||||
c = module.find('wxSize')
|
||||
|
||||
c.addProperty("width GetWidth SetWidth")
|
||||
c.addProperty("height GetHeight SetHeight")
|
||||
|
||||
# take care of the same issues as wxPoint
|
||||
etgtools.tweaker_tools.ignoreAllOperators(c)
|
||||
for f in c.find('operator+=').all() + \
|
||||
c.find('operator-=').all() + \
|
||||
c.find('operator*=').all() + \
|
||||
c.find('operator/=').all():
|
||||
f.ignore(False)
|
||||
wc = etgtools.WigCode("""\
|
||||
bool operator==(const wxSize& s1, const wxSize& s2);
|
||||
bool operator!=(const wxSize& s1, const wxSize& s2);
|
||||
wxSize operator*(const wxSize& s, int i);
|
||||
wxSize operator*(int i, const wxSize& s);
|
||||
wxSize operator+(const wxSize& s1, const wxSize& s2);
|
||||
wxSize operator-(const wxSize& s1, const wxSize& s2);
|
||||
wxSize operator/(const wxSize& s, int i);
|
||||
""")
|
||||
module.insertItemAfter(c, wc)
|
||||
|
||||
|
||||
#---------------------------------------
|
||||
# wxRect tweaks
|
||||
c = module.find('wxRect')
|
||||
|
||||
# take care of the same issues as wxPoint
|
||||
etgtools.tweaker_tools.ignoreAllOperators(c)
|
||||
for f in c.find('operator+=').all() + \
|
||||
c.find('operator*=').all():
|
||||
f.ignore(False)
|
||||
wc = etgtools.WigCode("""\
|
||||
bool operator==(const wxRect& r1, const wxRect& r2);
|
||||
bool operator!=(const wxRect& r1, const wxRect& r2);
|
||||
wxRect operator+(const wxRect& r1, const wxRect& r2);
|
||||
wxRect operator*(const wxRect& r1, const wxRect& r2);
|
||||
""")
|
||||
module.insertItemAfter(c, wc)
|
||||
|
||||
# These methods have some overloads that will end up with the same signature
|
||||
# in Python, so we have to remove one.
|
||||
module.find('wxRect.Deflate').findOverload(') const').ignore()
|
||||
module.find('wxRect.Inflate').findOverload(') const').ignore()
|
||||
module.find('wxRect.Union').findOverload(') const').ignore()
|
||||
module.find('wxRect.Intersect').findOverload(') const').ignore()
|
||||
|
||||
|
||||
#---------------------------------------
|
||||
# wxRealPoint tweaks
|
||||
c = module.find('wxRealPoint')
|
||||
|
||||
# take care of the same issues as wxPoint
|
||||
etgtools.tweaker_tools.ignoreAllOperators(c)
|
||||
for f in c.find('operator+=').all() + \
|
||||
c.find('operator-=').all():
|
||||
f.ignore(False)
|
||||
wc = etgtools.WigCode("""\
|
||||
bool operator==(const wxRealPoint& p1, const wxRealPoint& p2);
|
||||
bool operator!=(const wxRealPoint& p1, const wxRealPoint& p2);
|
||||
wxRealPoint operator*(const wxRealPoint& s, double i);
|
||||
wxRealPoint operator*(double i, const wxRealPoint& s);
|
||||
wxRealPoint operator+(const wxRealPoint& p1, const wxRealPoint& p2);
|
||||
wxRealPoint operator-(const wxRealPoint& p1, const wxRealPoint& p2);
|
||||
wxRealPoint operator/(const wxRealPoint& s, int i);
|
||||
""")
|
||||
module.insertItemAfter(c, wc)
|
||||
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
#---------------------------------------------------------------------------
|
||||
# Run the generators
|
||||
|
||||
# Create the code generator and make the wrapper code
|
||||
wg = etgtools.getWrapperGenerator()
|
||||
wg.generate(module)
|
||||
|
||||
# Create a documentation generator and let it do its thing
|
||||
dg = etgtools.getDocsGenerator()
|
||||
dg.generate(module)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
94
etg/geometry.py
Normal file
94
etg/geometry.py
Normal file
@@ -0,0 +1,94 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: geometry.py
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 4-Nov-2010
|
||||
# RCS-ID: $Id:$
|
||||
# Copyright: (c) 2010 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
PACKAGE = "wx"
|
||||
MODULE = "_core"
|
||||
NAME = "geometry"
|
||||
DOCSTRING = ""
|
||||
|
||||
# The classes and/or the basename of the Doxygen XML files to be processed by
|
||||
# this script.
|
||||
ITEMS = [
|
||||
'wxPoint2DDouble',
|
||||
'wxRect2DDouble',
|
||||
]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Parse the XML file(s) building a collection of Extractor objects
|
||||
|
||||
import etgtools
|
||||
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
|
||||
etgtools.parseDoxyXML(module, ITEMS)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Tweak the parsed meta objects in the module object as needed for customizing
|
||||
# the generated code and docstrings.
|
||||
|
||||
import etgtools.tweaker_tools
|
||||
etgtools.tweaker_tools.ignoreAssignmentOperators(module)
|
||||
etgtools.tweaker_tools.removeWxPrefixes(module)
|
||||
|
||||
|
||||
module.addHeaderCode('#include <wx/wx.h>')
|
||||
|
||||
|
||||
#---------------------------------------
|
||||
# wxPoint2D and wxRect2D tweaks
|
||||
|
||||
c = module.find('wxPoint2DDouble')
|
||||
c.pyName = 'Point2D'
|
||||
c.find('wxPoint2DDouble').findOverload('wxPoint2DInt').ignore()
|
||||
|
||||
c.find('m_x').pyName = 'x'
|
||||
c.find('m_y').pyName = 'y'
|
||||
c.find('GetFloor.x').out = True
|
||||
c.find('GetFloor.y').out = True
|
||||
c.find('GetRounded.x').out = True
|
||||
c.find('GetRounded.y').out = True
|
||||
|
||||
# these have link errors
|
||||
c.find('SetPolarCoordinates').ignore()
|
||||
c.find('operator/=').findOverload('wxDouble').ignore()
|
||||
c.find('operator*=').findOverload('wxDouble').ignore()
|
||||
|
||||
|
||||
# ignore these operator methods, since we are not wrapping the Int version
|
||||
c.find('operator*=').findOverload('wxInt32').ignore()
|
||||
c.find('operator/=').findOverload('wxInt32').ignore()
|
||||
|
||||
# ignore some of the global operators too
|
||||
for item in module:
|
||||
if isinstance(item, etgtools.FunctionDef) and item.type == 'wxPoint2DInt':
|
||||
item.ignore()
|
||||
if item.name in ['operator*', 'operator/'] and 'wxInt32' in item.argsString:
|
||||
item.ignore()
|
||||
|
||||
|
||||
c = module.find('wxRect2DDouble')
|
||||
c.pyName = 'Rect2D'
|
||||
c.find('m_x').pyName = 'x'
|
||||
c.find('m_y').pyName = 'y'
|
||||
c.find('m_width').pyName = 'width'
|
||||
c.find('m_height').pyName = 'height'
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
#---------------------------------------------------------------------------
|
||||
# Run the generators
|
||||
|
||||
# Create the code generator and make the wrapper code
|
||||
wg = etgtools.getWrapperGenerator()
|
||||
wg.generate(module)
|
||||
|
||||
# Create a documentation generator and let it do its thing
|
||||
dg = etgtools.getDocsGenerator()
|
||||
dg.generate(module)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
62
etg/object.py
Normal file
62
etg/object.py
Normal file
@@ -0,0 +1,62 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Name: object.py
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 9-Nov-2010
|
||||
# RCS-ID: $Id:$
|
||||
# Copyright: (c) 2010 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
PACKAGE = "wx"
|
||||
MODULE = "_core"
|
||||
NAME = "object" # Base name of the file to generate to for this script
|
||||
DOCSTRING = ""
|
||||
|
||||
# The classes and/or the basename of the Doxygen XML files to be processed by
|
||||
# this script.
|
||||
ITEMS = [
|
||||
'wxRefCounter',
|
||||
'wxObject',
|
||||
'wxClassInfo',
|
||||
]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Parse the XML file(s) building a collection of Extractor objects
|
||||
|
||||
import etgtools
|
||||
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
|
||||
etgtools.parseDoxyXML(module, ITEMS)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Tweak the parsed meta objects in the module object as needed for customizing
|
||||
# the generated code and docstrings.
|
||||
|
||||
import etgtools.tweaker_tools
|
||||
etgtools.tweaker_tools.ignoreAssignmentOperators(module)
|
||||
etgtools.tweaker_tools.removeWxPrefixes(module)
|
||||
|
||||
|
||||
module.find('wxObject.operator delete').ignore()
|
||||
module.find('wxObject.operator new').ignore()
|
||||
module.find('wxCreateDynamicObject').ignore()
|
||||
|
||||
|
||||
module.find('wxClassInfo').abstract = True
|
||||
module.find('wxClassInfo.wxClassInfo').ignore()
|
||||
|
||||
module.find('wxRefCounter.~wxRefCounter').ignore(False)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
#---------------------------------------------------------------------------
|
||||
# Run the generators
|
||||
|
||||
# Create the code generator and make the wrapper code
|
||||
wg = etgtools.getWrapperGenerator()
|
||||
wg.generate(module)
|
||||
|
||||
# Create a documentation generator and let it do its thing
|
||||
dg = etgtools.getDocsGenerator()
|
||||
dg.generate(module)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
50
etg/template.py
Normal file
50
etg/template.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Name:
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created:
|
||||
# RCS-ID: $Id:$
|
||||
# Copyright: (c) 2010 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
PACKAGE = ""
|
||||
MODULE = ""
|
||||
NAME = "" # Base name of the file to generate to for this script
|
||||
DOCSTRING = ""
|
||||
|
||||
# The classes and/or the basename of the Doxygen XML files to be processed by
|
||||
# this script.
|
||||
ITEMS = [ ]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Parse the XML file(s) building a collection of Extractor objects
|
||||
|
||||
import etgtools
|
||||
module = etgtools.ModuleDef(PACKAGE, MODULE, NAME, DOCSTRING)
|
||||
etgtools.parseDoxyXML(module, ITEMS)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Tweak the parsed meta objects in the module object as needed for customizing
|
||||
# the generated code and docstrings.
|
||||
|
||||
import etgtools.tweaker_tools
|
||||
etgtools.tweaker_tools.ignoreAssignmentOperators(module)
|
||||
etgtools.tweaker_tools.removeWxPrefixes(module)
|
||||
|
||||
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
#---------------------------------------------------------------------------
|
||||
# Run the generators
|
||||
|
||||
# Create the code generator and make the wrapper code
|
||||
wg = etgtools.getWrapperGenerator()
|
||||
wg.generate(module)
|
||||
|
||||
# Create a documentation generator and let it do its thing
|
||||
dg = etgtools.getDocsGenerator()
|
||||
dg.generate(module)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
109
etgtools/__init__.py
Normal file
109
etgtools/__init__.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
Classes and tools for describing the public API of wxWidgets, parsing
|
||||
them from the Doxygen XML, and producing wrapper code from them.
|
||||
"""
|
||||
import sys, os
|
||||
from extractors import *
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
phoenixRoot = os.path.abspath(os.path.split(__file__)[0]+'/..')
|
||||
xmlsrcbase = 'docs/doxygen/out/xml'
|
||||
WXWIN = os.environ.get('WXWIN')
|
||||
if not WXWIN:
|
||||
for rel in ['../wxWidgets', '..']:
|
||||
path = os.path.join(phoenixRoot, rel, xmlsrcbase)
|
||||
if path and os.path.exists(path):
|
||||
WXWIN = os.path.abspath(os.path.join(phoenixRoot, rel))
|
||||
break
|
||||
if WXWIN:
|
||||
XMLSRC = os.path.join(WXWIN, xmlsrcbase)
|
||||
assert WXWIN and os.path.exists(XMLSRC), "Unable to locate Doxygen XML files"
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
_filesparsed = set()
|
||||
|
||||
def parseDoxyXML(module, class_or_filename_list):
|
||||
"""
|
||||
Parse a list of Doxygen XML files and add the item(s) found there to the
|
||||
ModuleDef object.
|
||||
|
||||
If a name in the list a wx class name then the Doxygen XML filename is
|
||||
calculated from that name, otherwise it is treated as a filename in the
|
||||
Doxygen XML output folder.
|
||||
"""
|
||||
|
||||
def _classToDoxyName(name):
|
||||
import string
|
||||
filename = 'class'
|
||||
for c in name:
|
||||
if c in string.ascii_uppercase:
|
||||
filename += '_' + c.lower()
|
||||
else:
|
||||
filename += c
|
||||
return os.path.join(XMLSRC, filename) + '.xml'
|
||||
|
||||
def _includeToDoxyName(name):
|
||||
name = os.path.basename(name)
|
||||
name = name.replace('.h', '_8h')
|
||||
return os.path.join(XMLSRC, name) + '.xml', name + '.xml'
|
||||
|
||||
for class_or_filename in class_or_filename_list:
|
||||
pathname = _classToDoxyName(class_or_filename)
|
||||
if not os.path.exists(pathname):
|
||||
pathname = os.path.join(XMLSRC, class_or_filename)
|
||||
if verbose():
|
||||
print "Loading %s..." % pathname
|
||||
_filesparsed.add(pathname)
|
||||
|
||||
root = et.parse(pathname).getroot()
|
||||
for element in root:
|
||||
# extract and add top-level elements from the XML document
|
||||
item = module.addElement(element)
|
||||
|
||||
# Also automatically parse the XML for the include file to get related
|
||||
# typedefs, functions, enums, etc.
|
||||
if hasattr(item, 'includes'):
|
||||
for inc in item.includes:
|
||||
pathname, name = _includeToDoxyName(inc)
|
||||
if os.path.exists(pathname) \
|
||||
and pathname not in _filesparsed \
|
||||
and name not in class_or_filename_list:
|
||||
class_or_filename_list.append(name)
|
||||
|
||||
_filesparsed.clear()
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def getWrapperGenerator():
|
||||
"""
|
||||
A simple factory function to create a wrapper generator class of the desired type.
|
||||
"""
|
||||
if '--dump' in sys.argv:
|
||||
import generators
|
||||
gClass = generators.DumpWrapperGenerator
|
||||
elif '--swig' in sys.argv:
|
||||
import swig_generator
|
||||
gClass = swig_generator.SwigWrapperGenerator
|
||||
elif '--sip' in sys.argv:
|
||||
import sip_generator
|
||||
gClass = sip_generator.SipWrapperGenerator
|
||||
else:
|
||||
# The default is sip, at least for now...
|
||||
import sip_generator
|
||||
gClass = sip_generator.SipWrapperGenerator
|
||||
|
||||
return gClass()
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def getDocsGenerator():
|
||||
import generators
|
||||
g = generators.StubbedDocsGenerator()
|
||||
return g
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
|
||||
845
etgtools/extractors.py
Normal file
845
etgtools/extractors.py
Normal file
@@ -0,0 +1,845 @@
|
||||
# Functions and classes that can parse the Doxygen XML files and extract the
|
||||
# wxWidgets API info which we need from them.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import pprint
|
||||
import xml.etree.ElementTree as et
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# TODOs
|
||||
#
|
||||
# * Need a way to idicate that items are not available on certain platforms
|
||||
#
|
||||
# * Something for TypeHeader code for classes
|
||||
#
|
||||
# * something for associating typemap or convert to/from code for classes
|
||||
#
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# These classes simply hold various bits of information about the classes,
|
||||
# methods, functions and other items in the C/C++ API being wrapped.
|
||||
#
|
||||
# NOTE: Currently very little is being done with the docstrings. They can
|
||||
# either be reprocessed later by the document generator or we can do more
|
||||
# tinkering with them here. It just depends on decisions not yet made...
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class BaseDef(object):
|
||||
"""
|
||||
The base class for all element types and provides the common attributes
|
||||
and functions that they all share.
|
||||
"""
|
||||
nameTag = 'name'
|
||||
def __init__(self, element=None):
|
||||
self.name = '' # name of the item
|
||||
self.pyName = '' # rename to this name
|
||||
self.ignored = False # skip this item
|
||||
self.briefDoc = '' # either a string or a single para Element
|
||||
self.detailedDoc = [] # collection of para Elements
|
||||
|
||||
# The items list is used by some subclasses to collect items that are
|
||||
# part of that item, like methods of a ClassDef, etc.
|
||||
self.items = []
|
||||
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.items)
|
||||
|
||||
|
||||
def extract(self, element):
|
||||
# Pull info from the ElementTree element that is pertinent to this
|
||||
# class. Should be overridden in derived classes to get what each one
|
||||
# needs in addition to the base.
|
||||
self.name = element.find(self.nameTag).text
|
||||
bd = element.find('briefdescription')
|
||||
if len(bd):
|
||||
self.briefDoc = bd[0] # Should be just one <para> element
|
||||
self.detailedDoc = list(element.find('detaileddescription'))
|
||||
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
# debug helper, prints the extracted items
|
||||
txt = ("name: %s\n"
|
||||
"pyName: %s\n"
|
||||
"ignored: %s\n"
|
||||
"briefDoc: %s\n"
|
||||
"detailedDoc: %s\n" ) % \
|
||||
(self.name, self.pyName, self.ignored, self.briefDoc,
|
||||
_pf(self.detailedDoc, indent))
|
||||
_print(txt, indent, stream)
|
||||
|
||||
|
||||
def ignore(self, val=True):
|
||||
self.ignored = val
|
||||
|
||||
|
||||
def find(self, name):
|
||||
"""
|
||||
Locate and return an item within this item that has a matching name.
|
||||
The name string can use a dotted notation to continue the search
|
||||
recursively. Raises ExtractorError if not found.
|
||||
"""
|
||||
try:
|
||||
head, tail = name.split('.', 1)
|
||||
except ValueError:
|
||||
head, tail = name, None
|
||||
for item in self._findItems():
|
||||
if item.name == head or item.pyName == head:
|
||||
if not tail:
|
||||
return item
|
||||
else:
|
||||
return item.find(tail)
|
||||
else: # got though all items with no match
|
||||
raise ExtractorError("Unable to find item named '%s' within %s named '%s'" %
|
||||
(head, self.__class__.__name__, self.name))
|
||||
|
||||
|
||||
|
||||
def insertItem(self, index, item):
|
||||
self.items.insert(index, item)
|
||||
|
||||
def insertItemAfter(self, after, item):
|
||||
try:
|
||||
idx = self.items.index(after)
|
||||
self.items.insert(idx+1, item)
|
||||
except ValueError:
|
||||
self.items.append(item)
|
||||
|
||||
def insertItemBefore(self, before, item):
|
||||
try:
|
||||
idx = self.items.index(before)
|
||||
self.items.insert(idx, item)
|
||||
except ValueError:
|
||||
self.items.insert(0, item)
|
||||
|
||||
|
||||
|
||||
def allItems(self):
|
||||
"""
|
||||
Recursively create a sequence for traversing all items in the
|
||||
collection. A generator would be nice but just prebuilding a list will
|
||||
be good enough.
|
||||
"""
|
||||
items = [self]
|
||||
for item in self.items:
|
||||
items.extend(item.allItems())
|
||||
if hasattr(item, 'overloads'):
|
||||
for o in item.overloads:
|
||||
items.extend(o.allItems())
|
||||
return items
|
||||
|
||||
|
||||
def _findItems(self):
|
||||
# If there are more items to be searched than what is in self.items, a
|
||||
# subclass can override this to give a different list.
|
||||
return self.items
|
||||
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class VariableDef(BaseDef):
|
||||
"""
|
||||
Represents a basic variable declaration.
|
||||
"""
|
||||
def __init__(self, element=None, **kw):
|
||||
super(VariableDef, self).__init__()
|
||||
self.type = None
|
||||
self.definition = ''
|
||||
self.argsString = ''
|
||||
self.__dict__.update(**kw)
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
|
||||
def extract(self, element):
|
||||
super(VariableDef, self).extract(element)
|
||||
self.type = flattenNode(element.find('type'))
|
||||
self.definition = element.find('definition').text
|
||||
self.argsString = element.find('argsstring').text
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
super(VariableDef, self).dump(indent, stream)
|
||||
txt = ("type: %s\n"
|
||||
"definition: %s\n"
|
||||
"argsString: %s\n"
|
||||
) % (self.type, self.definition, self.argsString)
|
||||
_print(txt, indent, stream)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# These need the same attributes as VariableDef, but we use separate classes
|
||||
# so we can identify what kind of element it came from originally.
|
||||
|
||||
class GlobalVarDef(VariableDef):
|
||||
pass
|
||||
|
||||
class TypedefDef(VariableDef):
|
||||
pass
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class MemberVarDef(VariableDef):
|
||||
"""
|
||||
Represents a variable declaration in a class.
|
||||
"""
|
||||
def __init__(self, element=None, **kw):
|
||||
super(MemberVarDef, self).__init__()
|
||||
self.isStatic = False
|
||||
self.protection = ''
|
||||
self.__dict__.update(kw)
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
|
||||
def extract(self, element):
|
||||
super(MemberVarDef, self).extract(element)
|
||||
self.isStatic = element.get('static') == 'yes'
|
||||
self.protection = element.get('prot')
|
||||
assert self.protection in ['public', 'protected']
|
||||
# TODO: Should protected items be ignored by default or should we
|
||||
# leave that up to the tweaker code or the generators?
|
||||
if self.protection == 'protected':
|
||||
self.ignore()
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
super(MemberVarDef, self).dump(indent, stream)
|
||||
txt = ("isStatic: %s\n"
|
||||
"protection: %s\n"
|
||||
) % (self.isStatic, self.protection)
|
||||
_print(txt, indent, stream)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class FunctionDef(BaseDef):
|
||||
"""
|
||||
Information about a standalone function.
|
||||
"""
|
||||
def __init__(self, element=None, **kw):
|
||||
super(FunctionDef, self).__init__()
|
||||
self.type = None
|
||||
self.definition = ''
|
||||
self.argsString = ''
|
||||
self.pyArgsString = ''
|
||||
self.isOverloaded = False
|
||||
self.overloads = []
|
||||
self.deprecated = False # is the function deprecated
|
||||
self.factory = False # a factory function that creates a new instance of the return value
|
||||
self.pyReleaseGIL = False # release the Python GIL for this function call
|
||||
self.noCopy = False # don't make a copy of the return value, just wrap the original
|
||||
self.transfer = False # transfer ownership of return value to C++?
|
||||
self.transferBack = False # transfer ownership of return value from C++ to Python?
|
||||
self.transferThis = False # ownership of 'this' pointer transfered to this arg
|
||||
self.__dict__.update(kw)
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
|
||||
def releaseGIL(self, release=True):
|
||||
self.pyReleaseGIL = hold
|
||||
|
||||
def extract(self, element):
|
||||
super(FunctionDef, self).extract(element)
|
||||
self.type = flattenNode(element.find('type'))
|
||||
self.definition = element.find('definition').text
|
||||
self.argsString = element.find('argsstring').text
|
||||
for node in element.findall('param'):
|
||||
p = ParamDef(node)
|
||||
self.items.append(p)
|
||||
# TODO: Look at self.detailedDoc and pull out any matching
|
||||
# parameter description items and assign that value as the
|
||||
# briefDoc for this ParamDef object.
|
||||
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
super(FunctionDef, self).dump(indent, stream)
|
||||
txt = ("type: %s\n"
|
||||
"definition: %s\n"
|
||||
"argsString: %s\n"
|
||||
"pyArgsString: %s\n"
|
||||
"isOverloaded: %s\n"
|
||||
"deprecated: %s\n"
|
||||
"factory: %s\n"
|
||||
"releaseGIL: %s\n"
|
||||
"noCopy: %s\n"
|
||||
"transfer: %s\n"
|
||||
"transferBack: %s\n"
|
||||
"transferThis: %s\n"
|
||||
) % (self.type, self.definition, self.argsString, self.pyArgsString,
|
||||
self.isOverloaded, self.deprecated, self.factory, self.pyReleaseGIL,
|
||||
self.noCopy,
|
||||
self.transfer, self.transferBack, self.transferThis)
|
||||
_print(txt, indent, stream)
|
||||
if self.items:
|
||||
for p in self.items:
|
||||
assert isinstance(p, ParamDef)
|
||||
_print("ParamDef:", indent, stream)
|
||||
p.dump(indent+4, stream)
|
||||
_print("\n", indent, stream)
|
||||
if self.overloads:
|
||||
_print("Overloaded functions:", indent, stream)
|
||||
for f in self.overloads:
|
||||
_print("MethodDef:", indent+4, stream)
|
||||
f.dump(indent+8, stream)
|
||||
_print("\n", indent, stream)
|
||||
|
||||
|
||||
def checkForOverload(self, methods):
|
||||
for m in methods:
|
||||
if isinstance(m, MethodDef) and m.name == self.name:
|
||||
m.overloads.append(self)
|
||||
m.isOverloaded = self.isOverloaded = True
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def all(self):
|
||||
return [self] + self.overloads
|
||||
|
||||
|
||||
def findOverload(self, matchText):
|
||||
"""
|
||||
Search for an overloaded method that has matchText in its C++ argsString.
|
||||
"""
|
||||
for o in self.all():
|
||||
if matchText in o.argsString:
|
||||
return o
|
||||
return None
|
||||
|
||||
|
||||
def _findItems(self):
|
||||
items = list(self.items)
|
||||
for o in self.overloads:
|
||||
items.extend(o.items)
|
||||
return items
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class MethodDef(FunctionDef):
|
||||
"""
|
||||
Represents a class method, ctor or dtor declaration.
|
||||
"""
|
||||
def __init__(self, element=None, className=None, **kw):
|
||||
super(MethodDef, self).__init__()
|
||||
self.isVirtual = False
|
||||
self.isStatic = False
|
||||
self.isCtor = False
|
||||
self.isDtor = False
|
||||
self.protection = ''
|
||||
self.defaultCtor = False # use this ctor as the default one
|
||||
self.__dict__.update(kw)
|
||||
if element is not None:
|
||||
self.extract(element, className)
|
||||
|
||||
def extract(self, element, className):
|
||||
super(MethodDef, self).extract(element)
|
||||
self.isStatic = element.get('static') == 'yes'
|
||||
self.isVirtual = element.get('virt') == 'virtual'
|
||||
self.isCtor = self.name == className
|
||||
self.isDtor = self.name == '~' + className
|
||||
self.protection = element.get('prot')
|
||||
assert self.protection in ['public', 'protected']
|
||||
# TODO: Should protected items be ignored by default or should we
|
||||
# leave that up to the tweaker code or the generators?
|
||||
if self.protection == 'protected':
|
||||
self.ignore()
|
||||
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
super(MethodDef, self).dump(indent, stream)
|
||||
txt = ("isCtor: %s\n"
|
||||
"isDtor: %s\n"
|
||||
"isStatic: %s\n"
|
||||
"isVirtual: %s\n"
|
||||
"protection: %s\n"
|
||||
"defaultCtor: %s\n"
|
||||
) % (self.isCtor, self.isDtor, self.isStatic, self.isVirtual,
|
||||
self.protection, self.defaultCtor)
|
||||
_print(txt, indent, stream)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class ParamDef(BaseDef):
|
||||
"""
|
||||
A parameter of a function or method.
|
||||
"""
|
||||
def __init__(self, element=None, **kw):
|
||||
super(ParamDef, self).__init__()
|
||||
self.type = '' # data type
|
||||
self.default = '' # default value
|
||||
self.out = False # is it an output arg?
|
||||
self.inOut = False # is it both input and output?
|
||||
self.array = False # the param is to be treated as an array
|
||||
self.arraySize = False # the param is the size of the array
|
||||
self.transfer = False # transfer ownership of arg to C++?
|
||||
self.transferBack = False # transfer ownership of arg from C++ to Python?
|
||||
self.transferThis = False # ownership of 'this' pointer transfered to this arg
|
||||
self.__dict__.update(kw)
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
|
||||
def extract(self, element):
|
||||
self.name = element.find('declname').text
|
||||
self.type = flattenNode(element.find('type'))
|
||||
if element.find('defval') is not None:
|
||||
self.default = flattenNode(element.find('defval'))
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
#super(ParamDef, self).dump(indent, stream)
|
||||
txt = ("name: %s\n"
|
||||
"type: %s\n"
|
||||
"default: %s\n"
|
||||
"out: %s\n"
|
||||
"inOut: %s\n"
|
||||
"array: %s\n"
|
||||
"arraySize: %s\n"
|
||||
"transfer: %s\n"
|
||||
"transferBack: %s\n"
|
||||
"transferThis: %s\n"
|
||||
) % (self.name, self.type, self.default, self.out, self.inOut,
|
||||
self.array, self.arraySize,
|
||||
self.transfer, self.transferBack, self.transferThis)
|
||||
_print(txt, indent, stream)
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class ClassDef(BaseDef):
|
||||
"""
|
||||
The information about a class that is needed to generate wrappers for it.
|
||||
"""
|
||||
nameTag = 'compoundname'
|
||||
def __init__(self, element=None, **kw):
|
||||
super(ClassDef, self).__init__()
|
||||
self.bases = [] # base class names
|
||||
self.includes = [] # .h file for this class
|
||||
self.abstract = False # is it an abstract base class?
|
||||
self.deprecated = False # mark all methods as deprecated
|
||||
self.external = False # class is in another module
|
||||
self.noDefCtor = False # do not generate a default constructor
|
||||
self.singlton = False # class is a singleton so don't call the dtor until the interpreter exits
|
||||
self.convertFromPyObject = None
|
||||
self.convertToPyObject = None
|
||||
self.__dict__.update(kw)
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
|
||||
def extract(self, element):
|
||||
super(ClassDef, self).extract(element)
|
||||
|
||||
for node in element.findall('basecompoundref'):
|
||||
self.bases.append(node.text)
|
||||
for node in element.findall('includes'):
|
||||
self.includes.append(node.text)
|
||||
|
||||
# TODO: Is it possible for there to be memberdef's w/o a sectiondef?
|
||||
for node in element.findall('sectiondef/memberdef'):
|
||||
# skip any private items
|
||||
if node.get('prot') == 'private':
|
||||
continue
|
||||
kind = node.get('kind')
|
||||
if kind == 'function':
|
||||
m = MethodDef(node, self.name)
|
||||
if not m.checkForOverload(self.items):
|
||||
self.items.append(m)
|
||||
elif kind == 'variable':
|
||||
v = MemberVarDef(node)
|
||||
self.items.append(v)
|
||||
elif kind == 'enum':
|
||||
e = EnumDef(node)
|
||||
self.items.append(e)
|
||||
else:
|
||||
raise ExtractorError('Unknown memberdef kind: %s' % kind)
|
||||
|
||||
# TODO: do we need support for nested classes?
|
||||
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
super(ClassDef, self).dump(indent, stream)
|
||||
txt = ("bases: %s\n"
|
||||
"includes: %s\n"
|
||||
"abstract: %s\n"
|
||||
"deprecated: %s\n"
|
||||
"external: %s\n"
|
||||
"noDefCtor: %s\n"
|
||||
"singleton: %s\n"
|
||||
) % (_pf(self.bases, indent), _pf(self.includes, indent),
|
||||
self.abstract, self.deprecated, self.external, self.noDefCtor, self.singlton)
|
||||
_print(txt, indent, stream)
|
||||
for item in self.items:
|
||||
_print("%s:" % item.__class__.__name__, indent, stream)
|
||||
item.dump(indent+4, stream)
|
||||
_print("\n", indent, stream)
|
||||
|
||||
|
||||
def addProperty(self, *args, **kw):
|
||||
"""
|
||||
Add a property to a class, with a name, getter function and optionally
|
||||
a setter method.
|
||||
"""
|
||||
# As a convenience allow the name, getter and (optionally) the setter
|
||||
# to be passed as a single string. Otherwise the args will be passed
|
||||
# as-is to PropertyDef
|
||||
if len(args) == 1:
|
||||
name = getter = setter = ''
|
||||
split = args[0].split()
|
||||
assert len(split) in [2 ,3]
|
||||
if len(split) == 2:
|
||||
name, getter = split
|
||||
else:
|
||||
name, getter, setter = split
|
||||
p = PropertyDef(name, getter, setter, **kw)
|
||||
else:
|
||||
p = PropertyDef(*args, **kw)
|
||||
self.items.append(p)
|
||||
return p
|
||||
|
||||
|
||||
def addCppMethod(self, type, name, argsString, body, doc=None, **kw):
|
||||
"""
|
||||
Add a new C++ method to a class.
|
||||
"""
|
||||
md = CppMethodDef(type, name, argsString, body, doc, **kw)
|
||||
self.items.append(md)
|
||||
return md
|
||||
|
||||
def addCppCtor(self, argsString, body, doc=None, **kw):
|
||||
"""
|
||||
Add a C++ method that is a constructor.
|
||||
"""
|
||||
md = CppMethodDef('', self.name, argsString, body, doc=doc, isCtor=True, **kw)
|
||||
self.items.append(md)
|
||||
return md
|
||||
|
||||
|
||||
def addPyMethod(self, ):
|
||||
# TODO: Add the ability to inject a method implememnted in Python into
|
||||
# a wrapped class
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class EnumDef(BaseDef):
|
||||
"""
|
||||
A named or anonymous enumeration.
|
||||
"""
|
||||
def __init__(self, element=None, inClass=False, **kw):
|
||||
super(EnumDef, self).__init__()
|
||||
prot = element.get('prot')
|
||||
if prot is not None:
|
||||
self.protection = prot
|
||||
assert self.protection in ['public', 'protected']
|
||||
# TODO: Should protected items be ignored by default or should we
|
||||
# leave that up to the tweaker code or the generators?
|
||||
if self.protection == 'protected':
|
||||
self.ignore()
|
||||
self.__dict__.update(kw)
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
|
||||
def extract(self, element):
|
||||
super(EnumDef, self).extract(element)
|
||||
for node in element.findall('enumvalue'):
|
||||
value = EnumValueDef(node)
|
||||
self.items.append(value)
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
super(EnumDef, self).dump(indent, stream)
|
||||
for v in self.items:
|
||||
_print("EnumValueDef:", indent, stream)
|
||||
v.dump(indent+4, stream)
|
||||
_print("\n", indent, stream)
|
||||
|
||||
|
||||
|
||||
class EnumValueDef(BaseDef):
|
||||
"""
|
||||
An item in an enumeration.
|
||||
"""
|
||||
def __init__(self, element=None, **kw):
|
||||
super(EnumValueDef, self).__init__()
|
||||
if element is not None:
|
||||
self.extract(element)
|
||||
self.__dict__.update(kw)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class PropertyDef(BaseDef):
|
||||
"""
|
||||
Use the C++ methods of a class to make a Python property.
|
||||
|
||||
NOTE: This one is not automatically extracted, but can be added to
|
||||
classes in the tweaker stage
|
||||
"""
|
||||
def __init__(self, name, getter, setter=None, doc=None, **kw):
|
||||
super(PropertyDef, self).__init__()
|
||||
self.name = name
|
||||
self.getter = getter
|
||||
self.setter = setter
|
||||
self.briefDoc = doc
|
||||
self.protection = 'public'
|
||||
self.__dict__.update(kw)
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
# debug helper, prints the extracted items
|
||||
txt = ("name: %s\n"
|
||||
"getter: %s\n"
|
||||
"setter: %s\n"
|
||||
"briefDoc: %s\n") % \
|
||||
(self.name, self.getter, self.setter, self.briefDoc)
|
||||
_print(txt, indent, stream)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class CppMethodDef(MethodDef):
|
||||
"""
|
||||
This class provides information that can be used to add the code for a new
|
||||
method to a wrapper class that does not actually exist in the real C++
|
||||
class, or it can be used to provide an alternate implementation for a
|
||||
method that does exist. The backend generator support for this feature
|
||||
would be things like %extend in SWIG or %MethodCode in SIP.
|
||||
|
||||
NOTE: This one is not automatically extracted, but can be added to
|
||||
classes in the tweaker stage
|
||||
"""
|
||||
def __init__(self, type, name, argsString, body, doc=None, isCtor=False, **kw):
|
||||
super(CppMethodDef, self).__init__()
|
||||
self.type = type
|
||||
self.name = name
|
||||
self.argsString = argsString
|
||||
self.body = body
|
||||
self.briefDoc = doc
|
||||
self.isCtor = isCtor
|
||||
self.protection = 'public'
|
||||
self.__dict__.update(kw)
|
||||
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
# debug helper, prints the extracted items
|
||||
txt = ("type: %s\n"
|
||||
"name: %s\n"
|
||||
"argsString: %s\n"
|
||||
"briefDoc: %s\n"
|
||||
"body: %s\n") % \
|
||||
(self.type, self.name, self.argsString, self.briefDoc, self.body)
|
||||
_print(txt, indent, stream)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class WigCode(BaseDef):
|
||||
"""
|
||||
This class allows code defined by the triggers to be injected into the
|
||||
generated Wrapper Interface Generator file.
|
||||
"""
|
||||
def __init__(self, code, **kw):
|
||||
super(WigCode, self).__init__()
|
||||
self.code = code
|
||||
self.protection = 'public'
|
||||
self.__dict__.update(kw)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class ModuleDef(BaseDef):
|
||||
"""
|
||||
This class holds all the items that will be in the generated module
|
||||
"""
|
||||
def __init__(self, package, module, name, briefDoc='', detailedDoc=None):
|
||||
super(ModuleDef, self).__init__()
|
||||
self.package = package
|
||||
self.module = module
|
||||
self.name = name
|
||||
self.briefDoc = briefDoc
|
||||
if detailedDoc:
|
||||
self.detailedDoc = detailedDoc
|
||||
self.headerCode = []
|
||||
self.cppCode = []
|
||||
self.initializerCode = []
|
||||
self.preInitializerCode = []
|
||||
self.postInitializerCode = []
|
||||
self.includes = []
|
||||
self.imports = []
|
||||
|
||||
def addHeaderCode(self, code):
|
||||
if isinstance(code, list):
|
||||
self.headerCode.extend(code)
|
||||
else:
|
||||
self.headerCode.append(code)
|
||||
|
||||
def addCppCode(self, code):
|
||||
if isinstance(code, list):
|
||||
self.cppCode.extend(code)
|
||||
else:
|
||||
self.cppCode.append(code)
|
||||
|
||||
def addInitializerCode(self, code):
|
||||
if isinstance(code, list):
|
||||
self.initializerCode.extend(code)
|
||||
else:
|
||||
self.initializerCode.append(code)
|
||||
|
||||
def addPreInitializerCode(self, code):
|
||||
if isinstance(code, list):
|
||||
self.preInitializerCode.extend(code)
|
||||
else:
|
||||
self.preInitializerCode.append(code)
|
||||
|
||||
def addPostInitializerCode(self, code):
|
||||
if isinstance(code, list):
|
||||
self.postInitializerCode.extend(code)
|
||||
else:
|
||||
self.postInitializerCode.append(code)
|
||||
|
||||
def addInclude(self, name):
|
||||
if isinstance(name, list):
|
||||
self.includes.extend(name)
|
||||
else:
|
||||
self.includes.append(name)
|
||||
|
||||
def addImport(self, name):
|
||||
if isinstance(name, list):
|
||||
self.imports.extend(name)
|
||||
else:
|
||||
self.imports.append(name)
|
||||
|
||||
|
||||
def addElement(self, element):
|
||||
item = None
|
||||
kind = element.get('kind')
|
||||
if kind == 'class':
|
||||
extractingMsg(kind, element, ClassDef.nameTag)
|
||||
item = ClassDef(element)
|
||||
self.items.append(item)
|
||||
|
||||
elif kind == 'function':
|
||||
extractingMsg(kind, element)
|
||||
item = FunctionDef(element)
|
||||
if not item.checkForOverload(self.items):
|
||||
self.items.append(item)
|
||||
|
||||
elif kind == 'enum':
|
||||
extractingMsg(kind, element)
|
||||
item = EnumDef(element)
|
||||
self.items.append(item)
|
||||
|
||||
elif kind == 'variable':
|
||||
extractingMsg(kind, element)
|
||||
item = GlobalVarDef(element)
|
||||
self.items.append(item)
|
||||
|
||||
elif kind == 'typedef':
|
||||
extractingMsg(kind, element)
|
||||
item = TypedefDef(element)
|
||||
self.items.append(item)
|
||||
|
||||
elif kind == 'define':
|
||||
skippingMsg(kind, element)
|
||||
|
||||
elif kind == 'file':
|
||||
for node in element.findall('sectiondef/memberdef'):
|
||||
self.addElement(node)
|
||||
|
||||
else:
|
||||
raise ExtractorError('Unknown module item kind: %s' % kind)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
def dump(self, indent=0, stream=None):
|
||||
super(ModuleDef, self).dump(indent, stream)
|
||||
txt = ("includes: %s\n"
|
||||
"imports: %s\n"
|
||||
"headerCode: %s\n"
|
||||
"cppCode: %s\n"
|
||||
"pyCode: %s\n"
|
||||
"initializerCode: %s\n"
|
||||
) % (_pf(self.includes, indent),
|
||||
_pf(self.imports, indent),
|
||||
_pf(self.headerCode, indent),
|
||||
_pf(self.cppCode, indent),
|
||||
_pf(self.pyCode, indent),
|
||||
_pf(self.initializerCode, indent))
|
||||
_print(txt, indent, stream)
|
||||
_print('\n', indent, stream)
|
||||
|
||||
for item in self.items:
|
||||
_print(item.__class__.__name__+":", indent, stream)
|
||||
item.dump(indent+4, stream)
|
||||
_print('\n', indent, stream)
|
||||
|
||||
|
||||
def addCppFunction(self, type, name, argsString, body, doc=None, **kw):
|
||||
"""
|
||||
Add a new C++ function into the module that is written by hand, not
|
||||
wrapped.
|
||||
"""
|
||||
md = CppMethodDef(type, name, argsString, body, doc, **kw)
|
||||
self.items.append(md)
|
||||
return md
|
||||
|
||||
def addPyCode(self, code):
|
||||
# TODO: Add the ability to have some python code mingled with the
|
||||
# extension code. One way to do this is change the name of the %Module
|
||||
# (if there are any pyCode items) to be _module, and then generate a
|
||||
# module.py with this Python content, and have it do "from _module import *"
|
||||
# The user code would still "import module" but would end up getting access
|
||||
# to the combined items.
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Some helper functions and such
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
def flattenNode(node):
|
||||
"""
|
||||
Extract just the text from a node and its children, tossing out any child
|
||||
node tags and attributes.
|
||||
"""
|
||||
text = node.text or ""
|
||||
for n in node:
|
||||
text += flattenNode(n)
|
||||
if node.tail:
|
||||
text += node.tail.rstrip()
|
||||
return text.rstrip()
|
||||
|
||||
|
||||
class ExtractorError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def _print(value, indent, stream):
|
||||
if stream is None:
|
||||
stream = sys.stdout
|
||||
indent = ' ' * indent
|
||||
for line in str(value).splitlines():
|
||||
stream.write("%s%s\n" % (indent, line))
|
||||
|
||||
def _pf(item, indent):
|
||||
if indent == 0:
|
||||
indent = 4
|
||||
txt = pprint.pformat(item, indent)
|
||||
if '\n' in txt:
|
||||
txt = '\n' + txt
|
||||
return txt
|
||||
|
||||
|
||||
def verbose():
|
||||
return '--verbose' in sys.argv
|
||||
|
||||
def extractingMsg(kind, element, nameTag='name'):
|
||||
if verbose():
|
||||
print 'Extracting %s: %s' % (kind, element.find(nameTag).text)
|
||||
|
||||
def skippingMsg(kind, element):
|
||||
if verbose():
|
||||
print 'Skipping %s: %s' % (kind, element.find('name').text)
|
||||
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
34
etgtools/generators.py
Normal file
34
etgtools/generators.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#---------------------------------------------------------------------------
|
||||
# Just some base classes and stubs for the various generators
|
||||
|
||||
|
||||
class WrapperGeneratorBase(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
def generate(self, module, destFile=None):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class DumpWrapperGenerator(WrapperGeneratorBase):
|
||||
"""This one is just for debugging"""
|
||||
|
||||
def generate(self, module, destFile=None):
|
||||
print "\n----------------------------------------------------------"
|
||||
print "Dump of Module Objects"
|
||||
print "----------------------------------------------------------"
|
||||
module.dump()
|
||||
|
||||
|
||||
|
||||
class DocsGeneratorBase(object):
|
||||
def __init__(self):
|
||||
pass
|
||||
def generate(self, module):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class StubbedDocsGenerator(DocsGeneratorBase):
|
||||
def generate(self, module):
|
||||
pass
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
371
etgtools/sip_generator.py
Normal file
371
etgtools/sip_generator.py
Normal file
@@ -0,0 +1,371 @@
|
||||
import sys, os
|
||||
import extractors
|
||||
import generators
|
||||
from cStringIO import StringIO
|
||||
|
||||
|
||||
divider = '//' + '-'*75 + '\n'
|
||||
phoenixRoot = os.path.abspath(os.path.split(__file__)[0]+'/..')
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
class SipWrapperGenerator(generators.WrapperGeneratorBase):
|
||||
|
||||
def generate(self, module, destFile=None):
|
||||
stream = StringIO()
|
||||
|
||||
# generate SIP code from the module and its objects
|
||||
self.generateModule(module, stream)
|
||||
|
||||
# Write the contents of the stream to the destination file
|
||||
if not destFile:
|
||||
destFile = os.path.join(phoenixRoot, 'sip/gen', module.name + '.sip')
|
||||
file(destFile, 'wt').write(stream.getvalue())
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
def generateModule(self, module, stream):
|
||||
assert isinstance(module, extractors.ModuleDef)
|
||||
|
||||
# write the file header
|
||||
stream.write(divider + """\
|
||||
// This file is generated by wxPython's SIP generator. Do not edit by hand.
|
||||
//
|
||||
// Copyright: (c) 2010 by Total Control Software
|
||||
// Licence: wxWindows license
|
||||
""")
|
||||
if module.name == module.module:
|
||||
stream.write("""
|
||||
%%Module %s.%s
|
||||
%%Copying
|
||||
Copyright: (c) 2010 by Total Control Software
|
||||
Licence: wxWindows license
|
||||
%%End
|
||||
%%RealArgNames
|
||||
|
||||
""" % (module.package, module.name))
|
||||
|
||||
else:
|
||||
stream.write("//\n// This file is included from %s.sip\n//\n" % module.module)
|
||||
|
||||
stream.write(divider)
|
||||
|
||||
# %Imports and %Includes
|
||||
for i in module.imports:
|
||||
stream.write("%%Import %s.sip\n" % i)
|
||||
stream.write("\n")
|
||||
for i in module.includes:
|
||||
stream.write("%%Include %s.sip\n" % i)
|
||||
|
||||
# C++ code to be written out to the generated module
|
||||
if module.headerCode:
|
||||
stream.write("\n%ModuleHeaderCode\n")
|
||||
for c in module.headerCode:
|
||||
stream.write('%s\n' % c)
|
||||
stream.write("%End\n\n")
|
||||
|
||||
if module.cppCode:
|
||||
stream.write("%ModuleCode\n")
|
||||
for c in module.cppCode:
|
||||
stream.write('%s\n' % c)
|
||||
stream.write("%End\n")
|
||||
|
||||
stream.write('\n%s\n' % divider)
|
||||
|
||||
# Now generate each of the items in the module
|
||||
self.generateModuleItems(module, stream)
|
||||
|
||||
# Add code for the module initialization sections.
|
||||
if module.preInitializerCode:
|
||||
stream.write('\n%s\n\n%%PreInitialisationCode\n' % divider)
|
||||
for i in module.preInitializerCode:
|
||||
stream.write('%s\n' % i)
|
||||
stream.write('%End\n')
|
||||
if module.initializerCode:
|
||||
stream.write('\n%s\n\n%%InitialisationCode\n' % divider)
|
||||
for i in module.initializerCode:
|
||||
stream.write('%s\n' % i)
|
||||
stream.write('%End\n')
|
||||
if module.postInitializerCode:
|
||||
stream.write('\n%s\n\n%%PostInitialisationCode\n' % divider)
|
||||
for i in module.postInitializerCode:
|
||||
stream.write('%s\n' % i)
|
||||
stream.write('%End\n')
|
||||
|
||||
stream.write('\n%s\n' % divider)
|
||||
|
||||
|
||||
|
||||
def generateModuleItems(self, module, stream):
|
||||
methodMap = {
|
||||
extractors.ClassDef : self.generateClass,
|
||||
extractors.FunctionDef : self.generateFunction,
|
||||
extractors.EnumDef : self.generateEnum,
|
||||
extractors.GlobalVarDef : self.generateGlobalVar,
|
||||
extractors.TypedefDef : self.generateTypedef,
|
||||
extractors.WigCode : self.generateWigCode,
|
||||
}
|
||||
|
||||
for item in module:
|
||||
if item.ignored:
|
||||
continue
|
||||
function = methodMap[item.__class__]
|
||||
function(item, stream)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
def generateFunction(self, function, stream):
|
||||
assert isinstance(function, extractors.FunctionDef)
|
||||
if not function.ignored:
|
||||
stream.write('%s %s(' % (function.type, function.name))
|
||||
if function.items:
|
||||
stream.write('\n')
|
||||
self.generateParameters(function.items, stream, ' '*4)
|
||||
stream.write(')%s;\n' % self.annotate(function))
|
||||
for f in function.overloads:
|
||||
self.generateFunction(f, stream)
|
||||
stream.write('\n')
|
||||
|
||||
|
||||
def generateParameters(self, parameters, stream, indent):
|
||||
for idx, param in enumerate(parameters):
|
||||
if param.ignored:
|
||||
continue
|
||||
stream.write(indent)
|
||||
stream.write('%s %s' % (param.type, param.name))
|
||||
if param.default:
|
||||
stream.write(' = %s' % param.default)
|
||||
stream.write(self.annotate(param))
|
||||
if not idx == len(parameters)-1:
|
||||
stream.write(',')
|
||||
stream.write('\n')
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
def generateEnum(self, enum, stream, indent=''):
|
||||
assert isinstance(enum, extractors.EnumDef)
|
||||
if enum.ignored:
|
||||
return
|
||||
name = enum.name
|
||||
if name.startswith('@'):
|
||||
name = ''
|
||||
stream.write('%senum %s%s\n{\n' % (indent, name, self.annotate(enum)))
|
||||
values = []
|
||||
for v in enum.items:
|
||||
if v.ignored:
|
||||
continue
|
||||
values.append("%s %s%s" % (indent, v.name, self.annotate(v)))
|
||||
stream.write(',\n'.join(values))
|
||||
stream.write('%s\n};\n\n' % (indent, ))
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
def generateGlobalVar(self, globalVar, stream):
|
||||
assert isinstance(globalVar, extractors.GlobalVarDef)
|
||||
if globalVar.ignored:
|
||||
return
|
||||
stream.write('%s %s' % (globalVar.type, globalVar.name))
|
||||
stream.write('%s;\n\n' % self.annotate(globalVar))
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
def generateTypedef(self, typedef, stream):
|
||||
assert isinstance(typedef, extractors.TypedefDef)
|
||||
if typedef.ignored:
|
||||
return
|
||||
stream.write('typedef %s %s' % (typedef.type, typedef.name))
|
||||
stream.write('%s;\n\n' % self.annotate(typedef))
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
def generateWigCode(self, wig, stream, indent=''):
|
||||
assert isinstance(wig, extractors.WigCode)
|
||||
lines = [indent+line for line in wig.code.split('\n')]
|
||||
stream.write('\n'.join(lines))
|
||||
stream.write('\n\n')
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
def generateClass(self, klass, stream, indent=''):
|
||||
assert isinstance(klass, extractors.ClassDef)
|
||||
if klass.ignored:
|
||||
return
|
||||
|
||||
# write the class header
|
||||
stream.write('%sclass %s' % (indent, klass.name))
|
||||
if klass.bases:
|
||||
stream.write(' : ')
|
||||
stream.write(', '.join(klass.bases))
|
||||
stream.write(self.annotate(klass))
|
||||
stream.write('\n%s{\n' % indent)
|
||||
if klass.includes:
|
||||
stream.write('%s%%TypeHeaderCode\n' % indent)
|
||||
for inc in klass.includes:
|
||||
stream.write('%s #include <%s>\n' % (indent, inc))
|
||||
stream.write('%s%%End\n' % indent)
|
||||
stream.write('\n%spublic:\n' % indent)
|
||||
|
||||
# Split the items into public and protected groups
|
||||
ctors = [i for i in klass if
|
||||
isinstance(i, extractors.MethodDef) and
|
||||
i.protection == 'public' and (i.isCtor or i.isDtor)]
|
||||
public = [i for i in klass if i.protection == 'public' and i not in ctors]
|
||||
protected = [i for i in klass if i.protection == 'protected']
|
||||
|
||||
dispatch = {
|
||||
extractors.MemberVarDef : self.generateMemberVar,
|
||||
extractors.PropertyDef : self.generateProperty,
|
||||
extractors.MethodDef : self.generateMethod,
|
||||
extractors.EnumDef : self.generateEnum,
|
||||
extractors.CppMethodDef : self.generateCppMethod,
|
||||
extractors.WigCode : self.generateWigCode,
|
||||
# TODO: nested classes too?
|
||||
}
|
||||
for item in ctors:
|
||||
f = dispatch[item.__class__]
|
||||
f(item, stream, indent + ' '*4)
|
||||
|
||||
for item in public:
|
||||
f = dispatch[item.__class__]
|
||||
f(item, stream, indent + ' '*4)
|
||||
|
||||
if protected and [i for i in protected if not i.ignored]:
|
||||
stream.write('\nprotected:\n')
|
||||
for item in protected:
|
||||
f = dispatch[item.__class__]
|
||||
f(item, stream, indent + ' '*4)
|
||||
|
||||
if klass.convertFromPyObject:
|
||||
stream.write('%s%%ConvertToTypeCode\n' % indent)
|
||||
lines = [indent+l for l in klass.convertFromPyObject.split('\n')]
|
||||
stream.write('\n'.join(lines))
|
||||
stream.write('%s%%End\n' % indent)
|
||||
|
||||
if klass.convertToPyObject:
|
||||
stream.write('%s%%ConvertFromTypeCode\n' % indent)
|
||||
lines = [indent+l for l in klass.convertToPyObject.split('\n')]
|
||||
stream.write('\n'.join(lines))
|
||||
stream.write('%s%%End\n' % indent)
|
||||
|
||||
stream.write('%s}; // end of class %s\n\n\n' % (indent, klass.name))
|
||||
|
||||
|
||||
|
||||
|
||||
def generateMemberVar(self, memberVar, stream, indent):
|
||||
assert isinstance(memberVar, extractors.MemberVarDef)
|
||||
if memberVar.ignored:
|
||||
return
|
||||
stream.write('%s%s %s' % (indent, memberVar.type, memberVar.name))
|
||||
stream.write('%s;\n' % self.annotate(memberVar))
|
||||
|
||||
|
||||
def generateProperty(self, prop, stream, indent):
|
||||
assert isinstance(prop, extractors.PropertyDef)
|
||||
if prop.ignored:
|
||||
return
|
||||
stream.write('%s%%Property(name=%s, get=%s' % (indent, prop.name, prop.getter))
|
||||
if prop.setter:
|
||||
stream.write(', set=%s' % prop.setter)
|
||||
stream.write(')')
|
||||
if prop.briefDoc:
|
||||
stream.write(' // %s' % prop.briefDoc)
|
||||
stream.write('\n')
|
||||
|
||||
|
||||
def generateMethod(self, method, stream, indent):
|
||||
assert isinstance(method, extractors.MethodDef)
|
||||
if not method.ignored:
|
||||
if method.isVirtual:
|
||||
stream.write("%svirtual\n" % indent)
|
||||
if method.isStatic:
|
||||
stream.write("%sstatic\n" % indent)
|
||||
if method.isCtor or method.isDtor:
|
||||
stream.write('%s%s(' % (indent, method.name))
|
||||
else:
|
||||
stream.write('%s%s %s(' % (indent, method.type, method.name))
|
||||
if method.items:
|
||||
stream.write('\n')
|
||||
self.generateParameters(method.items, stream, indent+' '*4)
|
||||
stream.write(indent)
|
||||
stream.write(')%s;\n\n' % self.annotate(method))
|
||||
if method.overloads:
|
||||
for m in method.overloads:
|
||||
self.generateMethod(m, stream, indent)
|
||||
|
||||
|
||||
def generateCppMethod(self, method, stream, indent):
|
||||
assert isinstance(method, extractors.CppMethodDef)
|
||||
if method.ignored:
|
||||
return
|
||||
if method.isCtor:
|
||||
stream.write('%s%s%s%s;\n' %
|
||||
(indent, method.name, method.argsString, self.annotate(method)))
|
||||
else:
|
||||
stream.write('%s%s %s%s%s;\n' %
|
||||
(indent, method.type, method.name, method.argsString,
|
||||
self.annotate(method)))
|
||||
stream.write('%s%%MethodCode\n' % indent)
|
||||
lines = [indent+l for l in method.body.split('\n')]
|
||||
stream.write('\n'.join(lines))
|
||||
if len(lines) == 1:
|
||||
stream.write('\n%s' % indent)
|
||||
stream.write('%End\n\n')
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
def annotate(self, item):
|
||||
annotations = []
|
||||
if item.pyName:
|
||||
annotations.append('PyName=%s' % item.pyName)
|
||||
|
||||
if isinstance(item, extractors.ParamDef):
|
||||
if item.out:
|
||||
annotations.append('Out')
|
||||
if item.inOut:
|
||||
annotations.extend(['In', 'Out'])
|
||||
if item.array:
|
||||
annotations.append('Array')
|
||||
if item.arraySize:
|
||||
annotations.append('ArraySize')
|
||||
|
||||
if isinstance(item, (extractors.ParamDef, extractors.FunctionDef)):
|
||||
if item.transfer:
|
||||
annotations.append('Transfer')
|
||||
if item.transferBack:
|
||||
annotations.append('TransferBack')
|
||||
if item.transferThis:
|
||||
annotations.append('TranserThis')
|
||||
|
||||
if isinstance(item, extractors.FunctionDef):
|
||||
if item.deprecated:
|
||||
annotations.append('Deprecated')
|
||||
if item.factory:
|
||||
annotations.append('Factory')
|
||||
if item.pyReleaseGIL: # else HoldGIL??
|
||||
annotations.append('ReleaseGIL')
|
||||
if item.noCopy:
|
||||
annotations.append('NoCopy')
|
||||
|
||||
if isinstance(item, extractors.MethodDef):
|
||||
if item.defaultCtor:
|
||||
annotations.append('Default')
|
||||
|
||||
if isinstance(item, extractors.ClassDef):
|
||||
if item.abstract:
|
||||
annotations.append('Abstract')
|
||||
if item.deprecated:
|
||||
annotations.append('Deprecated')
|
||||
if item.external:
|
||||
annotations.append('External')
|
||||
if item.noDefCtor:
|
||||
annotations.append('NoDefaultCtors')
|
||||
if item.singlton:
|
||||
annotations.append('DelayDtor')
|
||||
|
||||
if annotations:
|
||||
return ' /%s/' % ', '.join(annotations)
|
||||
else:
|
||||
return ''
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
0
etgtools/sphynx_generator.py
Normal file
0
etgtools/sphynx_generator.py
Normal file
1
etgtools/swig_generator.py
Normal file
1
etgtools/swig_generator.py
Normal file
@@ -0,0 +1 @@
|
||||
# Move along, there's nothing to see here...
|
||||
42
etgtools/tweaker_tools.py
Normal file
42
etgtools/tweaker_tools.py
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
|
||||
import extractors
|
||||
|
||||
def removeWxPrefixes(node):
|
||||
"""
|
||||
Rename items with a 'wx' prefix to not have the prefix.
|
||||
"""
|
||||
for item in node.allItems():
|
||||
if not item.pyName \
|
||||
and item.name.startswith('wx') \
|
||||
and not item.name.startswith('wxEVT_') \
|
||||
and not isinstance(item, (extractors.TypedefDef,
|
||||
extractors.MethodDef )): # TODO: Any others?
|
||||
item.pyName = item.name[2:]
|
||||
|
||||
|
||||
|
||||
def ignoreAssignmentOperators(node):
|
||||
"""
|
||||
Set the ignored flag for all class methods that are assignment operators
|
||||
"""
|
||||
for item in node.allItems():
|
||||
if isinstance(item, extractors.MethodDef) and item.name == 'operator=':
|
||||
item.ignore()
|
||||
|
||||
|
||||
def ignoreAllOperators(node):
|
||||
"""
|
||||
Set the ignored flag for all class methods that are any kind of operator
|
||||
"""
|
||||
for item in node.allItems():
|
||||
if isinstance(item, extractors.MethodDef) and item.name.startswith('operator'):
|
||||
item.ignore()
|
||||
|
||||
|
||||
def createPyArgsStrings(node):
|
||||
"""
|
||||
TODO: Create a pythonized version of the argsString in function and method
|
||||
items that can be used as part of the docstring.
|
||||
"""
|
||||
pass
|
||||
176
setup.py
Normal file
176
setup.py
Normal file
@@ -0,0 +1,176 @@
|
||||
#----------------------------------------------------------------------
|
||||
# Name: setup.py
|
||||
# Purpose: Distutils build script for wxPython
|
||||
#
|
||||
# Author: Robin Dunn
|
||||
#
|
||||
# Created: 3-Nov-2010
|
||||
# Copyright: (c) 2010 by Total Control Software
|
||||
# Licence: wxWindows license
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
|
||||
import sys, os
|
||||
from distutils.core import setup, Extension
|
||||
from distutils.file_util import copy_file
|
||||
from distutils.dir_util import mkpath
|
||||
from distutils.dep_util import newer
|
||||
from distutils.spawn import spawn
|
||||
|
||||
from buildtools.config import Config, msg, opj
|
||||
import buildtools.distutils_hacks as hacks
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
NAME = "wxPython (phoenix)"
|
||||
DESCRIPTION = "Cross platform GUI toolkit for Python"
|
||||
AUTHOR = "Robin Dunn"
|
||||
AUTHOR_EMAIL = "Robin Dunn <robin@alldunn.com>"
|
||||
URL = "http://wxPython.org/"
|
||||
DOWNLOAD_URL = "http://wxPython.org/download.php"
|
||||
LICENSE = "wxWidgets Library License (LGPL derivative)"
|
||||
PLATFORMS = "WIN32,WIN64,OSX,POSIX"
|
||||
KEYWORDS = "GUI,wx,wxWindows,wxWidgets,cross-platform,awesome"
|
||||
|
||||
LONG_DESCRIPTION = """\
|
||||
wxPython is a GUI toolkit for Python that is a wrapper around the
|
||||
wxWidgets C++ GUI library. wxPython provides a large variety of
|
||||
window types and controls, all implemented with a native look and
|
||||
feel, by using the native widgets where possible.
|
||||
"""
|
||||
|
||||
CLASSIFIERS = """\
|
||||
Development Status :: 6 - Mature
|
||||
Environment :: MacOS X :: Carbon
|
||||
Environment :: Win32 (MS Windows)
|
||||
Environment :: Win64 (MS Windows)
|
||||
Environment :: X11 Applications :: GTK
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved
|
||||
Operating System :: MacOS :: MacOS X
|
||||
Operating System :: Microsoft :: Windows :: Windows 2000/XP/Vista/7
|
||||
Operating System :: POSIX
|
||||
Programming Language :: Python
|
||||
Topic :: Software Development :: User Interfaces
|
||||
"""
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# Create a buildtools.config.Configuration object
|
||||
cfg = Config()
|
||||
|
||||
|
||||
# Ensure that the directory containing this script is on the python
|
||||
# path for spawned commands so the builder and phoenix packages can be
|
||||
# found.
|
||||
thisdir = os.path.abspath(os.path.split(__file__)[0])
|
||||
os.environ['PYTHONPATH'] = thisdir + os.pathsep + os.environ.get('PYTHONPATH', '')
|
||||
|
||||
|
||||
WX_PKGLIST = [ cfg.PKGDIR ]
|
||||
|
||||
SCRIPTS = None
|
||||
DATA_FILES = []
|
||||
HEADERS = None
|
||||
BUILD_OPTIONS = { 'build_base' : cfg.BUILD_BASE }
|
||||
if cfg.WXPORT == 'msw':
|
||||
BUILD_OPTIONS[ 'compiler' ] = cfg.COMPILER
|
||||
|
||||
|
||||
|
||||
copy_file('src/__init__.py', cfg.PKGDIR, update=1, verbose=0)
|
||||
cfg.CLEANUP.append(opj(cfg.PKGDIR, '__init__.py'))
|
||||
|
||||
# update the license files
|
||||
mkpath('license')
|
||||
for file in ['preamble.txt', 'licence.txt', 'licendoc.txt', 'lgpl.txt']:
|
||||
copy_file(opj(cfg.WXDIR, 'docs', file), opj('license',file), update=1, verbose=0)
|
||||
cfg.CLEANUP.append(opj('license',file))
|
||||
cfg.CLEANUP.append('license')
|
||||
|
||||
|
||||
if sys.platform in ['win32', 'darwin']:
|
||||
cfg.build_locale_dir(opj(cfg.PKGDIR, 'locale'))
|
||||
DATA_FILES += cfg.build_locale_list(opj(cfg.PKGDIR, 'locale'))
|
||||
|
||||
|
||||
if os.name == 'nt':
|
||||
rc_file = ['src/wxc.rc']
|
||||
else:
|
||||
rc_file = []
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
extensions = [
|
||||
Extension('siplib', ['sip/siplib/apiversions.c',
|
||||
'sip/siplib/bool.cpp',
|
||||
'sip/siplib/descriptors.c',
|
||||
'sip/siplib/objmap.c',
|
||||
'sip/siplib/qtlib.c',
|
||||
'sip/siplib/siplib.c',
|
||||
'sip/siplib/threads.c',
|
||||
'sip/siplib/voidptr.c',
|
||||
],
|
||||
include_dirs = cfg.includes,
|
||||
define_macros = cfg.defines,
|
||||
library_dirs = cfg.libdirs,
|
||||
libraries = cfg.libs,
|
||||
extra_compile_args = cfg.cflags,
|
||||
extra_link_args = cfg.lflags,
|
||||
),
|
||||
|
||||
Extension('_core', ['etg/_core.py',
|
||||
'etg/object.py',
|
||||
'etg/gdicmn.py',
|
||||
'etg/geometry.py',
|
||||
],
|
||||
include_dirs = cfg.includes,
|
||||
define_macros = cfg.defines,
|
||||
library_dirs = cfg.libdirs,
|
||||
libraries = cfg.libs,
|
||||
extra_compile_args = cfg.cflags,
|
||||
extra_link_args = cfg.lflags,
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
if __name__ == '__main__':
|
||||
setup(name = NAME, #'wxPython',
|
||||
version = cfg.VERSION,
|
||||
description = DESCRIPTION,
|
||||
long_description = LONG_DESCRIPTION,
|
||||
author = AUTHOR,
|
||||
author_email = AUTHOR_EMAIL,
|
||||
url = URL,
|
||||
download_url = DOWNLOAD_URL,
|
||||
license = LICENSE,
|
||||
platforms = PLATFORMS,
|
||||
classifiers = [c for c in CLASSIFIERS.split("\n") if c],
|
||||
keywords = KEYWORDS,
|
||||
|
||||
packages = WX_PKGLIST,
|
||||
#extra_path = EXTRA_PATH,
|
||||
ext_package = cfg.PKGDIR,
|
||||
ext_modules = extensions,
|
||||
|
||||
options = { 'build' : BUILD_OPTIONS,
|
||||
'build_ext' : {'sip_opts' : cfg.SIPOPTS },
|
||||
},
|
||||
|
||||
scripts = SCRIPTS,
|
||||
data_files = DATA_FILES,
|
||||
headers = HEADERS,
|
||||
|
||||
# Override some of the default distutils command classes with my own
|
||||
cmdclass = { 'install' : hacks.wx_install,
|
||||
'install_data' : hacks.wx_smart_install_data,
|
||||
'install_headers' : hacks.wx_install_headers,
|
||||
'clean' : hacks.wx_extra_clean,
|
||||
'build_ext' : hacks.etgsip_build_ext,
|
||||
},
|
||||
|
||||
)
|
||||
3
sip/cpp/README.txt
Normal file
3
sip/cpp/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
This folder is where the ouput of SIP is placed. These will be the C++ source
|
||||
and header files, as well as a file for each module that specifies what files
|
||||
were generated for that module (which is used by the build scripts.)
|
||||
5
sip/gen/README.txt
Normal file
5
sip/gen/README.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
This directory is where wxPython's ETG scripts will deposit the
|
||||
wrapper generator files which describe the wxWidgets APIs that
|
||||
wxPython wraps. These files will be fed to SIP to procude the C++ code
|
||||
for the extension modules.
|
||||
|
||||
48
sip/siplib/LICENSE
Normal file
48
sip/siplib/LICENSE
Normal file
@@ -0,0 +1,48 @@
|
||||
RIVERBANK COMPUTING LIMITED LICENSE AGREEMENT FOR SIP
|
||||
|
||||
1. This LICENSE AGREEMENT is between Riverbank Computing Limited ("Riverbank"),
|
||||
and the Individual or Organization ("Licensee") accessing and otherwise using
|
||||
SIP software in source or binary form and its associated documentation. SIP
|
||||
comprises a software tool for generating Python bindings for software C and C++
|
||||
libraries, and a Python extension module used at runtime by those generated
|
||||
bindings.
|
||||
|
||||
2. Subject to the terms and conditions of this License Agreement, Riverbank
|
||||
hereby grants Licensee a nonexclusive, royalty-free, world-wide license to
|
||||
reproduce, analyze, test, perform and/or display publicly, prepare derivative
|
||||
works, distribute, and otherwise use SIP alone or in any derivative version,
|
||||
provided, however, that Riverbank's License Agreement and Riverbank's notice of
|
||||
copyright, e.g., "Copyright (c) 2010 Riverbank Computing Limited; All Rights
|
||||
Reserved" are retained in SIP alone or in any derivative version prepared by
|
||||
Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on or
|
||||
incorporates SIP or any part thereof, and wants to make the derivative work
|
||||
available to others as provided herein, then Licensee hereby agrees to include
|
||||
in any such work a brief summary of the changes made to SIP.
|
||||
|
||||
4. Licensee may not use SIP to generate Python bindings for any C or C++
|
||||
library for which bindings are already provided by Riverbank.
|
||||
|
||||
5. Riverbank is making SIP available to Licensee on an "AS IS" basis.
|
||||
RIVERBANK MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY
|
||||
OF EXAMPLE, BUT NOT LIMITATION, RIVERBANK MAKES NO AND DISCLAIMS ANY
|
||||
REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR
|
||||
PURPOSE OR THAT THE USE OF SIP WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
|
||||
|
||||
6. RIVERBANK SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF SIP FOR ANY
|
||||
INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING,
|
||||
DISTRIBUTING, OR OTHERWISE USING SIP, OR ANY DERIVATIVE THEREOF, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY THEREOF.
|
||||
|
||||
7. This License Agreement will automatically terminate upon a material breach
|
||||
of its terms and conditions.
|
||||
|
||||
8. Nothing in this License Agreement shall be deemed to create any relationship
|
||||
of agency, partnership, or joint venture between Riverbank and Licensee. This
|
||||
License Agreement does not grant permission to use Riverbank trademarks or
|
||||
trade name in a trademark sense to endorse or promote products or services of
|
||||
Licensee, or any third party.
|
||||
|
||||
9. By copying, installing or otherwise using SIP, Licensee agrees to be bound
|
||||
by the terms and conditions of this License Agreement.
|
||||
11
sip/siplib/README.txt
Normal file
11
sip/siplib/README.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
This folder contains a copy of the SIP runtime library code. It is
|
||||
here so we can make it part of the wxPython build instead of needing
|
||||
to have a dependency upon SIP already being installed on user
|
||||
machines.
|
||||
|
||||
3rd party extension modules that need to use or interact with wxPython
|
||||
types or other items will need to ensure that they #include the sip.h
|
||||
located in this folder so they will know the proper module name to
|
||||
import to find this version of the runtime library. This feature was
|
||||
added in SIP 4.12.
|
||||
|
||||
295
sip/siplib/apiversions.c
Normal file
295
sip/siplib/apiversions.c
Normal file
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* The implementation of the supprt for setting API versions.
|
||||
*
|
||||
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
||||
*
|
||||
* This file is part of SIP.
|
||||
*
|
||||
* This copy of SIP is licensed for use under the terms of the SIP License
|
||||
* Agreement. See the file LICENSE for more details.
|
||||
*
|
||||
* This copy of SIP may also used under the terms of the GNU General Public
|
||||
* License v2 or v3 as published by the Free Software Foundation which can be
|
||||
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
||||
*
|
||||
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "sip.h"
|
||||
#include "sipint.h"
|
||||
|
||||
|
||||
/*
|
||||
* The structure that defines the version number of an API.
|
||||
*/
|
||||
typedef struct _apiVersionDef {
|
||||
/* The name of the API. */
|
||||
const char *api_name;
|
||||
|
||||
/*
|
||||
* The version number of the API. This will either be set explicitly via
|
||||
* a call to sip.setapi() or implicitly by an imported module.
|
||||
*/
|
||||
int version_nr;
|
||||
|
||||
/* The next in the list of APIs. */
|
||||
struct _apiVersionDef *next;
|
||||
} apiVersionDef;
|
||||
|
||||
|
||||
/*
|
||||
* The list of API versions.
|
||||
*/
|
||||
static apiVersionDef *api_versions = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Forward declarations.
|
||||
*/
|
||||
static int add_api(const char *api, int version_nr);
|
||||
static apiVersionDef *find_api(const char *api);
|
||||
|
||||
|
||||
/*
|
||||
* See if a range of versions of a particular API is enabled.
|
||||
*/
|
||||
int sip_api_is_api_enabled(const char *name, int from, int to)
|
||||
{
|
||||
const apiVersionDef *avd;
|
||||
|
||||
if ((avd = find_api(name)) == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (from > 0 && avd->version_nr < from)
|
||||
return FALSE;
|
||||
|
||||
if (to > 0 && avd->version_nr >= to)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialise the the API for a module and return a negative value on error.
|
||||
*/
|
||||
int sipInitAPI(sipExportedModuleDef *em, PyObject *mod_dict)
|
||||
{
|
||||
int *apis, i;
|
||||
sipVersionedFunctionDef *vf;
|
||||
sipTypeDef **tdp;
|
||||
|
||||
/* See if the module defines any APIs. */
|
||||
if ((apis = em->em_versions) != NULL)
|
||||
{
|
||||
while (apis[0] >= 0)
|
||||
{
|
||||
/*
|
||||
* See if it is an API definition rather than a range
|
||||
* definition.
|
||||
*/
|
||||
if (apis[2] < 0)
|
||||
{
|
||||
const char *api_name;
|
||||
const apiVersionDef *avd;
|
||||
|
||||
api_name = sipNameFromPool(em, apis[0]);
|
||||
|
||||
/* Use the default version if not already set explicitly. */
|
||||
if ((avd = find_api(api_name)) == NULL)
|
||||
if (add_api(api_name, apis[1]) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
apis += 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add any versioned global functions to the module dictionary. */
|
||||
if ((vf = em->em_versioned_functions) != NULL)
|
||||
{
|
||||
while (vf->vf_name >= 0)
|
||||
{
|
||||
if (sipIsRangeEnabled(em, vf->vf_api_range))
|
||||
{
|
||||
const char *func_name = sipNameFromPool(em, vf->vf_name);
|
||||
PyMethodDef *pmd;
|
||||
PyObject *py_func;
|
||||
|
||||
if ((pmd = sip_api_malloc(sizeof (PyMethodDef))) == NULL)
|
||||
return -1;
|
||||
|
||||
pmd->ml_name = SIP_MLNAME_CAST(func_name);
|
||||
pmd->ml_meth = vf->vf_function;
|
||||
pmd->ml_flags = vf->vf_flags;
|
||||
pmd->ml_doc = vf->vf_docstring;
|
||||
|
||||
if ((py_func = PyCFunction_New(pmd, NULL)) == NULL)
|
||||
return -1;
|
||||
|
||||
if (PyDict_SetItemString(mod_dict, func_name, py_func) < 0)
|
||||
{
|
||||
Py_DECREF(py_func);
|
||||
return -1;
|
||||
}
|
||||
|
||||
Py_DECREF(py_func);
|
||||
}
|
||||
|
||||
++vf;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the types table according to any version information. */
|
||||
for (tdp = em->em_types, i = 0; i < em->em_nrtypes; ++i, ++tdp)
|
||||
{
|
||||
sipTypeDef *td;
|
||||
|
||||
if ((td = *tdp) != NULL && td->td_version >= 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (sipIsRangeEnabled(em, td->td_version))
|
||||
{
|
||||
/* Update the type with the enabled version. */
|
||||
*tdp = td;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while ((td = td->td_next_version) != NULL);
|
||||
|
||||
/*
|
||||
* If there is no enabled version then stub the disabled version
|
||||
* so that we don't lose the name from the (sorted) types table.
|
||||
*/
|
||||
if (td == NULL)
|
||||
sipTypeSetStub(*tdp);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get the version number for an API.
|
||||
*/
|
||||
PyObject *sipGetAPI(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *api;
|
||||
const apiVersionDef *avd;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s:getapi", &api))
|
||||
return NULL;
|
||||
|
||||
if ((avd = find_api(api)) == NULL)
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError, "unknown API '%s'", api);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return PyLong_FromLong(avd->version_nr);
|
||||
#else
|
||||
return PyInt_FromLong(avd->version_nr);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the version number for an API.
|
||||
*/
|
||||
PyObject *sipSetAPI(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *api;
|
||||
int version_nr;
|
||||
const apiVersionDef *avd;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "si:setapi", &api, &version_nr))
|
||||
return NULL;
|
||||
|
||||
if (version_nr < 1)
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"API version numbers must be greater or equal to 1, not %d",
|
||||
version_nr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((avd = find_api(api)) == NULL)
|
||||
{
|
||||
char *api_copy;
|
||||
|
||||
/* Make a deep copy of the name. */
|
||||
if ((api_copy = sip_api_malloc(strlen(api) + 1)) == NULL)
|
||||
return NULL;
|
||||
|
||||
strcpy(api_copy, api);
|
||||
|
||||
if (add_api(api_copy, version_nr) < 0)
|
||||
return NULL;
|
||||
}
|
||||
else if (avd->version_nr != version_nr)
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"API '%s' has already been set to version %d", api,
|
||||
avd->version_nr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add a new API to the global list returning a negative value on error.
|
||||
*/
|
||||
static int add_api(const char *api, int version_nr)
|
||||
{
|
||||
apiVersionDef *avd;
|
||||
|
||||
if ((avd = sip_api_malloc(sizeof (apiVersionDef))) == NULL)
|
||||
return -1;
|
||||
|
||||
avd->api_name = api;
|
||||
avd->version_nr = version_nr;
|
||||
avd->next = api_versions;
|
||||
|
||||
api_versions = avd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the definition for the given API, or NULL if there was none.
|
||||
*/
|
||||
static apiVersionDef *find_api(const char *api)
|
||||
{
|
||||
apiVersionDef *avd;
|
||||
|
||||
for (avd = api_versions; avd != NULL; avd = avd->next)
|
||||
if (strcmp(avd->api_name, api) == 0)
|
||||
break;
|
||||
|
||||
return avd;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return TRUE if a range defined by a range index is enabled.
|
||||
*/
|
||||
int sipIsRangeEnabled(sipExportedModuleDef *em, int range_index)
|
||||
{
|
||||
int *range = &em->em_versions[range_index * 3];
|
||||
const char *api_name = sipNameFromPool(em, range[0]);
|
||||
|
||||
return sip_api_is_api_enabled(api_name, range[1], range[2]);
|
||||
}
|
||||
22
sip/siplib/bool.cpp
Normal file
22
sip/siplib/bool.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
// This contains all the C++ code that is needed by the sip module.
|
||||
//
|
||||
// Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
||||
//
|
||||
// This file is part of SIP.
|
||||
//
|
||||
// This copy of SIP is licensed for use under the terms of the SIP License
|
||||
// Agreement. See the file LICENSE for more details.
|
||||
//
|
||||
// This copy of SIP may also used under the terms of the GNU General Public
|
||||
// License v2 or v3 as published by the Free Software Foundation which can be
|
||||
// found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
||||
//
|
||||
// SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
// Set a C++ bool for the main C implementation of the module.
|
||||
extern "C" void sipSetBool(void *ptr, int val)
|
||||
{
|
||||
*reinterpret_cast<bool *>(ptr) = val;
|
||||
}
|
||||
305
sip/siplib/descriptors.c
Normal file
305
sip/siplib/descriptors.c
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* The implementation of the different descriptors.
|
||||
*
|
||||
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
||||
*
|
||||
* This file is part of SIP.
|
||||
*
|
||||
* This copy of SIP is licensed for use under the terms of the SIP License
|
||||
* Agreement. See the file LICENSE for more details.
|
||||
*
|
||||
* This copy of SIP may also used under the terms of the GNU General Public
|
||||
* License v2 or v3 as published by the Free Software Foundation which can be
|
||||
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
||||
*
|
||||
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "sip.h"
|
||||
#include "sipint.h"
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* A method descriptor. We don't use the similar Python descriptor because it
|
||||
* doesn't support a method having static and non-static overloads.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/* Forward declarations of slots. */
|
||||
static PyObject *sipMethodDescr_descr_get(PyObject *self, PyObject *obj,
|
||||
PyObject *type);
|
||||
static PyObject *sipMethodDescr_repr(PyObject *self);
|
||||
|
||||
|
||||
/*
|
||||
* The object data structure.
|
||||
*/
|
||||
typedef struct _sipMethodDescr {
|
||||
PyObject_HEAD
|
||||
|
||||
/* The method definition. */
|
||||
PyMethodDef *pmd;
|
||||
} sipMethodDescr;
|
||||
|
||||
|
||||
/*
|
||||
* The type data structure.
|
||||
*/
|
||||
PyTypeObject sipMethodDescr_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"sip.methoddescriptor", /* tp_name */
|
||||
sizeof (sipMethodDescr), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
0, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
sipMethodDescr_repr, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
sipMethodDescr_descr_get, /* tp_descr_get */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Return a new method descriptor for the given method.
|
||||
*/
|
||||
PyObject *sipMethodDescr_New(PyMethodDef *pmd)
|
||||
{
|
||||
PyObject *descr = PyType_GenericAlloc(&sipMethodDescr_Type, 0);
|
||||
|
||||
if (descr != NULL)
|
||||
((sipMethodDescr *)descr)->pmd = pmd;
|
||||
|
||||
return descr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The descriptor's descriptor get slot.
|
||||
*/
|
||||
static PyObject *sipMethodDescr_descr_get(PyObject *self, PyObject *obj,
|
||||
PyObject *type)
|
||||
{
|
||||
sipMethodDescr *md = (sipMethodDescr *)self;
|
||||
|
||||
if (obj == Py_None)
|
||||
obj = NULL;
|
||||
|
||||
return PyCFunction_New(md->pmd, obj);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The descriptor's repr slot. This is for the benefit of cProfile which seems
|
||||
* to determine attribute names differently to the rest of Python.
|
||||
*/
|
||||
static PyObject *sipMethodDescr_repr(PyObject *self)
|
||||
{
|
||||
sipMethodDescr *md = (sipMethodDescr *)self;
|
||||
|
||||
return
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
PyUnicode_FromFormat
|
||||
#else
|
||||
PyString_FromFormat
|
||||
#endif
|
||||
("<built-in method %s>", md->pmd->ml_name);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* A variable descriptor. We don't use the similar Python descriptor because
|
||||
* it doesn't support static variables.
|
||||
*****************************************************************************/
|
||||
|
||||
|
||||
/* Forward declarations of slots. */
|
||||
static PyObject *sipVariableDescr_descr_get(PyObject *self, PyObject *obj,
|
||||
PyObject *type);
|
||||
static int sipVariableDescr_descr_set(PyObject *self, PyObject *obj,
|
||||
PyObject *value);
|
||||
|
||||
|
||||
/*
|
||||
* The object data structure.
|
||||
*/
|
||||
typedef struct _sipVariableDescr {
|
||||
PyObject_HEAD
|
||||
|
||||
/* The getter/setter definition. */
|
||||
sipVariableDef *vd;
|
||||
|
||||
/* The generated type definition. */
|
||||
const sipTypeDef *td;
|
||||
|
||||
/* The generated container definition. */
|
||||
const sipContainerDef *cod;
|
||||
} sipVariableDescr;
|
||||
|
||||
|
||||
/*
|
||||
* The type data structure.
|
||||
*/
|
||||
PyTypeObject sipVariableDescr_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"sip.variabledescriptor", /* tp_name */
|
||||
sizeof (sipVariableDescr), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
0, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_compare */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
0, /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
sipVariableDescr_descr_get, /* tp_descr_get */
|
||||
sipVariableDescr_descr_set, /* tp_descr_set */
|
||||
};
|
||||
|
||||
|
||||
/* Forward declarations. */
|
||||
static int get_instance_address(sipVariableDescr *vd, PyObject *obj,
|
||||
void **addrp);
|
||||
|
||||
|
||||
/*
|
||||
* Return a new method descriptor for the given getter/setter.
|
||||
*/
|
||||
PyObject *sipVariableDescr_New(sipVariableDef *vd, const sipTypeDef *td,
|
||||
const sipContainerDef *cod)
|
||||
{
|
||||
PyObject *descr = PyType_GenericAlloc(&sipVariableDescr_Type, 0);
|
||||
|
||||
if (descr != NULL)
|
||||
{
|
||||
((sipVariableDescr *)descr)->vd = vd;
|
||||
((sipVariableDescr *)descr)->td = td;
|
||||
((sipVariableDescr *)descr)->cod = cod;
|
||||
}
|
||||
|
||||
return descr;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The descriptor's descriptor get slot.
|
||||
*/
|
||||
static PyObject *sipVariableDescr_descr_get(PyObject *self, PyObject *obj,
|
||||
PyObject *type)
|
||||
{
|
||||
sipVariableDescr *vd = (sipVariableDescr *)self;
|
||||
void *addr;
|
||||
|
||||
if (get_instance_address(vd, obj, &addr) < 0)
|
||||
return NULL;
|
||||
|
||||
return ((sipVariableGetterFunc)vd->vd->vd_getter)(addr, type);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The descriptor's descriptor set slot.
|
||||
*/
|
||||
static int sipVariableDescr_descr_set(PyObject *self, PyObject *obj,
|
||||
PyObject *value)
|
||||
{
|
||||
sipVariableDescr *vd = (sipVariableDescr *)self;
|
||||
void *addr;
|
||||
|
||||
/* Check that the value isn't const. */
|
||||
if (vd->vd->vd_setter == NULL)
|
||||
{
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%s' object attribute '%s' is read-only",
|
||||
sipPyNameOfContainer(vd->cod, vd->td), vd->vd->vd_name);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (get_instance_address(vd, obj, &addr) < 0)
|
||||
return -1;
|
||||
|
||||
return ((sipVariableSetterFunc)vd->vd->vd_setter)(addr, value, obj);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the C/C++ address of any instance.
|
||||
*/
|
||||
static int get_instance_address(sipVariableDescr *vd, PyObject *obj,
|
||||
void **addrp)
|
||||
{
|
||||
void *addr;
|
||||
|
||||
if (vd->vd->vd_type == ClassVariable)
|
||||
{
|
||||
addr = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Check that access was via an instance. */
|
||||
if (obj == NULL || obj == Py_None)
|
||||
{
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%s' object attribute '%s' is an instance attribute",
|
||||
sipPyNameOfContainer(vd->cod, vd->td), vd->vd->vd_name);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the C++ instance. */
|
||||
if ((addr = sip_api_get_cpp_ptr((sipSimpleWrapper *)obj, vd->td)) == NULL)
|
||||
return -1;
|
||||
}
|
||||
|
||||
*addrp = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
307
sip/siplib/objmap.c
Normal file
307
sip/siplib/objmap.c
Normal file
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* This module implements a hash table class for mapping C/C++ addresses to the
|
||||
* corresponding wrapped Python object.
|
||||
*
|
||||
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
||||
*
|
||||
* This file is part of SIP.
|
||||
*
|
||||
* This copy of SIP is licensed for use under the terms of the SIP License
|
||||
* Agreement. See the file LICENSE for more details.
|
||||
*
|
||||
* This copy of SIP may also used under the terms of the GNU General Public
|
||||
* License v2 or v3 as published by the Free Software Foundation which can be
|
||||
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
||||
*
|
||||
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "sip.h"
|
||||
#include "sipint.h"
|
||||
|
||||
|
||||
#define hash_1(k,s) (((unsigned long)(k)) % (s))
|
||||
#define hash_2(k,s) ((s) - 2 - (hash_1((k),(s)) % ((s) - 2)))
|
||||
|
||||
|
||||
/* Prime numbers to use as hash table sizes. */
|
||||
static unsigned long hash_primes[] = {
|
||||
521, 1031, 2053, 4099,
|
||||
8209, 16411, 32771, 65537, 131101, 262147,
|
||||
524309, 1048583, 2097169, 4194319, 8388617, 16777259,
|
||||
33554467, 67108879, 134217757, 268435459, 536870923, 1073741827,
|
||||
2147483659U,0
|
||||
};
|
||||
|
||||
|
||||
static sipHashEntry *newHashTable(unsigned long);
|
||||
static sipHashEntry *findHashEntry(sipObjectMap *,void *);
|
||||
static void reorganiseMap(sipObjectMap *om);
|
||||
static void *getUnguardedPointer(sipSimpleWrapper *w);
|
||||
|
||||
|
||||
/*
|
||||
* Initialise an object map.
|
||||
*/
|
||||
void sipOMInit(sipObjectMap *om)
|
||||
{
|
||||
om -> primeIdx = 0;
|
||||
om -> unused = om -> size = hash_primes[om -> primeIdx];
|
||||
om -> stale = 0;
|
||||
om -> hash_array = newHashTable(om -> size);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Finalise an object map.
|
||||
*/
|
||||
void sipOMFinalise(sipObjectMap *om)
|
||||
{
|
||||
sip_api_free(om -> hash_array);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Allocate and initialise a new hash table.
|
||||
*/
|
||||
static sipHashEntry *newHashTable(unsigned long size)
|
||||
{
|
||||
size_t nbytes;
|
||||
sipHashEntry *hashtab;
|
||||
|
||||
nbytes = sizeof (sipHashEntry) * size;
|
||||
|
||||
if ((hashtab = (sipHashEntry *)sip_api_malloc(nbytes)) != NULL)
|
||||
memset(hashtab,0,nbytes);
|
||||
|
||||
return hashtab;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a pointer to the hash entry that is used, or should be used, for the
|
||||
* given C/C++ address.
|
||||
*/
|
||||
static sipHashEntry *findHashEntry(sipObjectMap *om,void *key)
|
||||
{
|
||||
unsigned long hash, inc;
|
||||
void *hek;
|
||||
|
||||
hash = hash_1(key,om -> size);
|
||||
inc = hash_2(key,om -> size);
|
||||
|
||||
while ((hek = om -> hash_array[hash].key) != NULL && hek != key)
|
||||
hash = (hash + inc) % om -> size;
|
||||
|
||||
return &om -> hash_array[hash];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the wrapped Python object of a specific type for a C/C++ address or
|
||||
* NULL if it wasn't found.
|
||||
*/
|
||||
sipSimpleWrapper *sipOMFindObject(sipObjectMap *om, void *key,
|
||||
const sipTypeDef *td)
|
||||
{
|
||||
sipHashEntry *he = findHashEntry(om, key);
|
||||
sipSimpleWrapper *sw;
|
||||
PyTypeObject *py_type = sipTypeAsPyTypeObject(td);
|
||||
|
||||
/* Go through each wrapped object at this address. */
|
||||
for (sw = he->first; sw != NULL; sw = sw->next)
|
||||
{
|
||||
/*
|
||||
* If the reference count is 0 then it is in the process of being
|
||||
* deleted, so ignore it. It's not completely clear how this can
|
||||
* happen (but it can) because it implies that the garbage collection
|
||||
* code is being re-entered (and there are guards in place to prevent
|
||||
* this).
|
||||
*/
|
||||
if (Py_REFCNT(sw) == 0)
|
||||
continue;
|
||||
|
||||
/* Ignore it if the C/C++ address is no longer valid. */
|
||||
if (sip_api_get_address(sw) == NULL)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If this wrapped object is of the given type, or a sub-type of it,
|
||||
* then we assume it is the same C++ object.
|
||||
*/
|
||||
if (PyObject_TypeCheck(sw, py_type))
|
||||
return sw;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add a C/C++ address and the corresponding wrapped Python object to the map.
|
||||
*/
|
||||
void sipOMAddObject(sipObjectMap *om, sipSimpleWrapper *val)
|
||||
{
|
||||
sipHashEntry *he = findHashEntry(om, getUnguardedPointer(val));
|
||||
|
||||
/*
|
||||
* If the bucket is in use then we appear to have several objects at the
|
||||
* same address.
|
||||
*/
|
||||
if (he->first != NULL)
|
||||
{
|
||||
/*
|
||||
* This can happen for three reasons. A variable of one class can be
|
||||
* declared at the start of another class. Therefore there are two
|
||||
* objects, of different classes, with the same address. The second
|
||||
* reason is that the old C/C++ object has been deleted by C/C++ but we
|
||||
* didn't get to find out for some reason, and a new C/C++ instance has
|
||||
* been created at the same address. The third reason is if we are in
|
||||
* the process of deleting a Python object but the C++ object gets
|
||||
* wrapped again because the C++ dtor called a method that has been
|
||||
* re-implemented in Python. The absence of the SIP_SHARE_MAP flag
|
||||
* tells us that a new C++ instance has just been created and so we
|
||||
* know the second reason is the correct one so we mark the old
|
||||
* pointers as invalid and reuse the entry. Otherwise we just add this
|
||||
* one to the existing list of objects at this address.
|
||||
*/
|
||||
if (!(val->flags & SIP_SHARE_MAP))
|
||||
{
|
||||
sipSimpleWrapper *sw = he->first;
|
||||
|
||||
he->first = NULL;
|
||||
|
||||
while (sw != NULL)
|
||||
{
|
||||
sipSimpleWrapper *next = sw->next;
|
||||
|
||||
/* We are removing it from the map here. */
|
||||
sipSetNotInMap(sw);
|
||||
sip_api_common_dtor(sw);
|
||||
|
||||
sw = next;
|
||||
}
|
||||
}
|
||||
|
||||
val->next = he->first;
|
||||
he->first = val;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* See if the bucket was unused or stale. */
|
||||
if (he->key == NULL)
|
||||
{
|
||||
he->key = getUnguardedPointer(val);
|
||||
om->unused--;
|
||||
}
|
||||
else
|
||||
om->stale--;
|
||||
|
||||
/* Add the rest of the new value. */
|
||||
he->first = val;
|
||||
val->next = NULL;
|
||||
|
||||
reorganiseMap(om);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reorganise a map if it is running short of space.
|
||||
*/
|
||||
static void reorganiseMap(sipObjectMap *om)
|
||||
{
|
||||
unsigned long old_size, i;
|
||||
sipHashEntry *ohe, *old_tab;
|
||||
|
||||
/* Don't bother if it still has more than 12% available. */
|
||||
if (om -> unused > om -> size >> 3)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If reorganising (ie. making the stale buckets unused) using the same
|
||||
* sized table would make 25% available then do that. Otherwise use a
|
||||
* bigger table (if possible).
|
||||
*/
|
||||
if (om -> unused + om -> stale < om -> size >> 2 && hash_primes[om -> primeIdx + 1] != 0)
|
||||
om -> primeIdx++;
|
||||
|
||||
old_size = om -> size;
|
||||
old_tab = om -> hash_array;
|
||||
|
||||
om -> unused = om -> size = hash_primes[om -> primeIdx];
|
||||
om -> stale = 0;
|
||||
om -> hash_array = newHashTable(om -> size);
|
||||
|
||||
/* Transfer the entries from the old table to the new one. */
|
||||
ohe = old_tab;
|
||||
|
||||
for (i = 0; i < old_size; ++i)
|
||||
{
|
||||
if (ohe -> key != NULL && ohe -> first != NULL)
|
||||
{
|
||||
*findHashEntry(om,ohe -> key) = *ohe;
|
||||
om -> unused--;
|
||||
}
|
||||
|
||||
++ohe;
|
||||
}
|
||||
|
||||
sip_api_free(old_tab);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Remove a C/C++ object from the table. Return 0 if it was removed
|
||||
* successfully.
|
||||
*/
|
||||
int sipOMRemoveObject(sipObjectMap *om, sipSimpleWrapper *val)
|
||||
{
|
||||
sipHashEntry *he;
|
||||
sipSimpleWrapper **swp;
|
||||
void *addr;
|
||||
|
||||
/* Handle the trivial case. */
|
||||
if (sipNotInMap(val))
|
||||
return 0;
|
||||
|
||||
if ((addr = getUnguardedPointer(val)) == NULL)
|
||||
return 0;
|
||||
|
||||
he = findHashEntry(om, addr);
|
||||
|
||||
for (swp = &he->first; *swp != NULL; swp = &(*swp)->next)
|
||||
if (*swp == val)
|
||||
{
|
||||
*swp = val->next;
|
||||
|
||||
/*
|
||||
* If the bucket is now empty then count it as stale. Note that we
|
||||
* do not NULL the key and count it as unused because that might
|
||||
* throw out the search for another entry that wanted to go here,
|
||||
* found it already occupied, and was put somewhere else. In other
|
||||
* words, searches must be repeatable until we reorganise the
|
||||
* table.
|
||||
*/
|
||||
if (he->first == NULL)
|
||||
om->stale++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the unguarded pointer to a C/C++ instance, ie. the pointer was valid
|
||||
* but may longer be.
|
||||
*/
|
||||
static void *getUnguardedPointer(sipSimpleWrapper *w)
|
||||
{
|
||||
return (w->access_func != NULL) ? w->access_func(w, UnguardedPointer) : w->data;
|
||||
}
|
||||
659
sip/siplib/qtlib.c
Normal file
659
sip/siplib/qtlib.c
Normal file
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
* The SIP library code that implements the interface to the optional module
|
||||
* supplied Qt support.
|
||||
*
|
||||
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
||||
*
|
||||
* This file is part of SIP.
|
||||
*
|
||||
* This copy of SIP is licensed for use under the terms of the SIP License
|
||||
* Agreement. See the file LICENSE for more details.
|
||||
*
|
||||
* This copy of SIP may also used under the terms of the GNU General Public
|
||||
* License v2 or v3 as published by the Free Software Foundation which can be
|
||||
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
||||
*
|
||||
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
|
||||
#include <Python.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sip.h"
|
||||
#include "sipint.h"
|
||||
|
||||
|
||||
/* This is how Qt "types" signals and slots. */
|
||||
#define isQtSlot(s) (*(s) == '1')
|
||||
#define isQtSignal(s) (*(s) == '2')
|
||||
|
||||
|
||||
static PyObject *getWeakRef(PyObject *obj);
|
||||
static char *sipStrdup(const char *);
|
||||
static void *createUniversalSlot(sipWrapper *txSelf, const char *sig,
|
||||
PyObject *rxObj, const char *slot, const char **member, int flags);
|
||||
static void *findSignal(void *txrx, const char **sig);
|
||||
static void *newSignal(void *txrx, const char **sig);
|
||||
|
||||
|
||||
/*
|
||||
* Find an existing signal.
|
||||
*/
|
||||
static void *findSignal(void *txrx, const char **sig)
|
||||
{
|
||||
if (sipQtSupport->qt_find_universal_signal != NULL)
|
||||
txrx = sipQtSupport->qt_find_universal_signal(txrx, sig);
|
||||
|
||||
return txrx;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a usable signal, creating a new universal signal if needed.
|
||||
*/
|
||||
static void *newSignal(void *txrx, const char **sig)
|
||||
{
|
||||
void *new_txrx = findSignal(txrx, sig);
|
||||
|
||||
if (new_txrx == NULL && sipQtSupport->qt_create_universal_signal != NULL)
|
||||
new_txrx = sipQtSupport->qt_create_universal_signal(txrx, sig);
|
||||
|
||||
return new_txrx;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a universal slot. Returns a pointer to it or 0 if there was an
|
||||
* error.
|
||||
*/
|
||||
static void *createUniversalSlot(sipWrapper *txSelf, const char *sig,
|
||||
PyObject *rxObj, const char *slot, const char **member, int flags)
|
||||
{
|
||||
void *us = sipQtSupport->qt_create_universal_slot(txSelf, sig, rxObj, slot,
|
||||
member, flags);
|
||||
|
||||
if (us && txSelf)
|
||||
sipSetPossibleProxy((sipSimpleWrapper *)txSelf);
|
||||
|
||||
return us;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Invoke a single slot (Qt or Python) and return the result.
|
||||
*/
|
||||
PyObject *sip_api_invoke_slot(const sipSlot *slot, PyObject *sigargs)
|
||||
{
|
||||
PyObject *sa, *oxtype, *oxvalue, *oxtb, *sfunc, *sref;
|
||||
|
||||
/* Keep some compilers quiet. */
|
||||
oxtype = oxvalue = oxtb = NULL;
|
||||
|
||||
/* Fan out Qt signals. (Only PyQt3 will do this.) */
|
||||
if (slot->name != NULL && slot->name[0] != '\0')
|
||||
{
|
||||
assert(sipQtSupport->qt_emit_signal);
|
||||
|
||||
if (sipQtSupport->qt_emit_signal(slot->pyobj, slot->name, sigargs) < 0)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/* Get the object to call, resolving any weak references. */
|
||||
if (slot->weakSlot == Py_True)
|
||||
{
|
||||
/*
|
||||
* The slot is guaranteed to be Ok because it has an extra reference or
|
||||
* is None.
|
||||
*/
|
||||
sref = slot->pyobj;
|
||||
Py_INCREF(sref);
|
||||
}
|
||||
else if (slot -> weakSlot == NULL)
|
||||
sref = NULL;
|
||||
else if ((sref = PyWeakref_GetObject(slot -> weakSlot)) == NULL)
|
||||
return NULL;
|
||||
else
|
||||
Py_INCREF(sref);
|
||||
|
||||
if (sref == Py_None)
|
||||
{
|
||||
/*
|
||||
* If the real object has gone then we pretend everything is Ok. This
|
||||
* mimics the Qt behaviour of not caring if a receiving object has been
|
||||
* deleted.
|
||||
*/
|
||||
Py_DECREF(sref);
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
if (slot -> pyobj == NULL)
|
||||
{
|
||||
PyObject *self = (sref != NULL ? sref : slot->meth.mself);
|
||||
|
||||
/*
|
||||
* If the receiver wraps a C++ object then ignore the call if it no
|
||||
* longer exists.
|
||||
*/
|
||||
if (PyObject_TypeCheck(self, (PyTypeObject *)&sipSimpleWrapper_Type) &&
|
||||
sip_api_get_address((sipSimpleWrapper *)self) == NULL)
|
||||
{
|
||||
Py_XDECREF(sref);
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
sfunc = PyMethod_New(slot->meth.mfunc, self);
|
||||
#else
|
||||
sfunc = PyMethod_New(slot->meth.mfunc, self, slot->meth.mclass);
|
||||
#endif
|
||||
|
||||
if (sfunc == NULL)
|
||||
{
|
||||
Py_XDECREF(sref);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (slot -> name != NULL)
|
||||
{
|
||||
char *mname = slot -> name + 1;
|
||||
PyObject *self = (sref != NULL ? sref : slot->pyobj);
|
||||
|
||||
if ((sfunc = PyObject_GetAttrString(self, mname)) == NULL || !PyCFunction_Check(sfunc))
|
||||
{
|
||||
/*
|
||||
* Note that in earlier versions of SIP this error would be
|
||||
* detected when the slot was connected.
|
||||
*/
|
||||
PyErr_Format(PyExc_NameError,"Invalid slot %s",mname);
|
||||
|
||||
Py_XDECREF(sfunc);
|
||||
Py_XDECREF(sref);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sfunc = slot->pyobj;
|
||||
Py_INCREF(sfunc);
|
||||
}
|
||||
|
||||
/*
|
||||
* We make repeated attempts to call a slot. If we work out that it failed
|
||||
* because of an immediate type error we try again with one less argument.
|
||||
* We keep going until we run out of arguments to drop. This emulates the
|
||||
* Qt ability of the slot to accept fewer arguments than a signal provides.
|
||||
*/
|
||||
sa = sigargs;
|
||||
Py_INCREF(sa);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
PyObject *nsa, *xtype, *xvalue, *xtb, *resobj;
|
||||
|
||||
if ((resobj = PyEval_CallObject(sfunc, sa)) != NULL)
|
||||
{
|
||||
Py_DECREF(sfunc);
|
||||
Py_XDECREF(sref);
|
||||
|
||||
/* Remove any previous exception. */
|
||||
|
||||
if (sa != sigargs)
|
||||
{
|
||||
Py_XDECREF(oxtype);
|
||||
Py_XDECREF(oxvalue);
|
||||
Py_XDECREF(oxtb);
|
||||
PyErr_Clear();
|
||||
}
|
||||
|
||||
Py_DECREF(sa);
|
||||
|
||||
return resobj;
|
||||
}
|
||||
|
||||
/* Get the exception. */
|
||||
PyErr_Fetch(&xtype,&xvalue,&xtb);
|
||||
|
||||
/*
|
||||
* See if it is unacceptable. An acceptable failure is a type error
|
||||
* with no traceback - so long as we can still reduce the number of
|
||||
* arguments and try again.
|
||||
*/
|
||||
if (!PyErr_GivenExceptionMatches(xtype,PyExc_TypeError) ||
|
||||
xtb != NULL ||
|
||||
PyTuple_GET_SIZE(sa) == 0)
|
||||
{
|
||||
/*
|
||||
* If there is a traceback then we must have called the slot and
|
||||
* the exception was later on - so report the exception as is.
|
||||
*/
|
||||
if (xtb != NULL)
|
||||
{
|
||||
if (sa != sigargs)
|
||||
{
|
||||
Py_XDECREF(oxtype);
|
||||
Py_XDECREF(oxvalue);
|
||||
Py_XDECREF(oxtb);
|
||||
}
|
||||
|
||||
PyErr_Restore(xtype,xvalue,xtb);
|
||||
}
|
||||
else if (sa == sigargs)
|
||||
PyErr_Restore(xtype,xvalue,xtb);
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Discard the latest exception and restore the original one.
|
||||
*/
|
||||
Py_XDECREF(xtype);
|
||||
Py_XDECREF(xvalue);
|
||||
Py_XDECREF(xtb);
|
||||
|
||||
PyErr_Restore(oxtype,oxvalue,oxtb);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/* If this is the first attempt, save the exception. */
|
||||
if (sa == sigargs)
|
||||
{
|
||||
oxtype = xtype;
|
||||
oxvalue = xvalue;
|
||||
oxtb = xtb;
|
||||
}
|
||||
else
|
||||
{
|
||||
Py_XDECREF(xtype);
|
||||
Py_XDECREF(xvalue);
|
||||
Py_XDECREF(xtb);
|
||||
}
|
||||
|
||||
/* Create the new argument tuple. */
|
||||
if ((nsa = PyTuple_GetSlice(sa,0,PyTuple_GET_SIZE(sa) - 1)) == NULL)
|
||||
{
|
||||
/* Tidy up. */
|
||||
Py_XDECREF(oxtype);
|
||||
Py_XDECREF(oxvalue);
|
||||
Py_XDECREF(oxtb);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Py_DECREF(sa);
|
||||
sa = nsa;
|
||||
}
|
||||
|
||||
Py_DECREF(sfunc);
|
||||
Py_XDECREF(sref);
|
||||
|
||||
Py_DECREF(sa);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compare two slots to see if they are the same.
|
||||
*/
|
||||
int sip_api_same_slot(const sipSlot *sp, PyObject *rxObj, const char *slot)
|
||||
{
|
||||
/* See if they are signals or Qt slots, ie. they have a name. */
|
||||
if (slot != NULL)
|
||||
{
|
||||
if (sp->name == NULL || sp->name[0] == '\0')
|
||||
return 0;
|
||||
|
||||
return (sipQtSupport->qt_same_name(sp->name, slot) && sp->pyobj == rxObj);
|
||||
}
|
||||
|
||||
/* See if they are pure Python methods. */
|
||||
if (PyMethod_Check(rxObj))
|
||||
{
|
||||
if (sp->pyobj != NULL)
|
||||
return 0;
|
||||
|
||||
return (sp->meth.mfunc == PyMethod_GET_FUNCTION(rxObj)
|
||||
&& sp->meth.mself == PyMethod_GET_SELF(rxObj)
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
&& sp->meth.mclass == PyMethod_GET_CLASS(rxObj)
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
/* See if they are wrapped C++ methods. */
|
||||
if (PyCFunction_Check(rxObj))
|
||||
{
|
||||
if (sp->name == NULL || sp->name[0] != '\0')
|
||||
return 0;
|
||||
|
||||
return (sp->pyobj == PyCFunction_GET_SELF(rxObj) &&
|
||||
strcmp(&sp->name[1], ((PyCFunctionObject *)rxObj)->m_ml->ml_name) == 0);
|
||||
}
|
||||
|
||||
/* The objects must be the same. */
|
||||
return (sp->pyobj == rxObj);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert a valid Python signal or slot to an existing universal slot.
|
||||
*/
|
||||
void *sipGetRx(sipSimpleWrapper *txSelf, const char *sigargs, PyObject *rxObj,
|
||||
const char *slot, const char **memberp)
|
||||
{
|
||||
if (slot != NULL)
|
||||
if (isQtSlot(slot) || isQtSignal(slot))
|
||||
{
|
||||
void *rx;
|
||||
|
||||
*memberp = slot;
|
||||
|
||||
if ((rx = sip_api_get_cpp_ptr((sipSimpleWrapper *)rxObj, sipQObjectType)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (isQtSignal(slot))
|
||||
rx = findSignal(rx, memberp);
|
||||
|
||||
return rx;
|
||||
}
|
||||
|
||||
/*
|
||||
* The slot was either a Python callable or PyQt3 Python signal so there
|
||||
* should be a universal slot.
|
||||
*/
|
||||
return sipQtSupport->qt_find_slot(sip_api_get_address(txSelf), sigargs, rxObj, slot, memberp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert a Python receiver (either a Python signal or slot or a Qt signal or
|
||||
* slot) to a Qt receiver. It is only ever called when the signal is a Qt
|
||||
* signal. Return NULL is there was an error.
|
||||
*/
|
||||
void *sip_api_convert_rx(sipWrapper *txSelf, const char *sigargs,
|
||||
PyObject *rxObj, const char *slot, const char **memberp, int flags)
|
||||
{
|
||||
if (slot == NULL)
|
||||
return createUniversalSlot(txSelf, sigargs, rxObj, NULL, memberp, flags);
|
||||
|
||||
if (isQtSlot(slot) || isQtSignal(slot))
|
||||
{
|
||||
void *rx;
|
||||
|
||||
*memberp = slot;
|
||||
|
||||
if ((rx = sip_api_get_cpp_ptr((sipSimpleWrapper *)rxObj, sipQObjectType)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (isQtSignal(slot))
|
||||
rx = newSignal(rx, memberp);
|
||||
|
||||
return rx;
|
||||
}
|
||||
|
||||
/* The slot is a Python signal so we need a universal slot to catch it. */
|
||||
return createUniversalSlot(txSelf, sigargs, rxObj, slot, memberp, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Connect a Qt signal or a Python signal to a Qt slot, a Qt signal, a Python
|
||||
* slot or a Python signal. This is all possible combinations.
|
||||
*/
|
||||
PyObject *sip_api_connect_rx(PyObject *txObj, const char *sig, PyObject *rxObj,
|
||||
const char *slot, int type)
|
||||
{
|
||||
/* Handle Qt signals. */
|
||||
if (isQtSignal(sig))
|
||||
{
|
||||
void *tx, *rx;
|
||||
const char *member, *real_sig;
|
||||
int res;
|
||||
|
||||
if ((tx = sip_api_get_cpp_ptr((sipSimpleWrapper *)txObj, sipQObjectType)) == NULL)
|
||||
return NULL;
|
||||
|
||||
real_sig = sig;
|
||||
|
||||
if ((tx = newSignal(tx, &real_sig)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if ((rx = sip_api_convert_rx((sipWrapper *)txObj, sig, rxObj, slot, &member, 0)) == NULL)
|
||||
return NULL;
|
||||
|
||||
res = sipQtSupport->qt_connect(tx, real_sig, rx, member, type);
|
||||
|
||||
return PyBool_FromLong(res);
|
||||
}
|
||||
|
||||
/* Handle Python signals. Only PyQt3 will get this far. */
|
||||
assert(sipQtSupport->qt_connect_py_signal);
|
||||
|
||||
if (sipQtSupport->qt_connect_py_signal(txObj, sig, rxObj, slot) < 0)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(Py_True);
|
||||
return Py_True;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Disconnect a signal to a signal or a Qt slot.
|
||||
*/
|
||||
PyObject *sip_api_disconnect_rx(PyObject *txObj,const char *sig,
|
||||
PyObject *rxObj,const char *slot)
|
||||
{
|
||||
/* Handle Qt signals. */
|
||||
if (isQtSignal(sig))
|
||||
{
|
||||
sipSimpleWrapper *txSelf = (sipSimpleWrapper *)txObj;
|
||||
void *tx, *rx;
|
||||
const char *member;
|
||||
int res;
|
||||
|
||||
if ((tx = sip_api_get_cpp_ptr(txSelf, sipQObjectType)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if ((rx = sipGetRx(txSelf, sig, rxObj, slot, &member)) == NULL)
|
||||
{
|
||||
Py_INCREF(Py_False);
|
||||
return Py_False;
|
||||
}
|
||||
|
||||
/* Handle Python signals. */
|
||||
tx = findSignal(tx, &sig);
|
||||
|
||||
res = sipQtSupport->qt_disconnect(tx, sig, rx, member);
|
||||
|
||||
/*
|
||||
* Delete it if it is a universal slot as this will be it's only
|
||||
* connection. If the slot is actually a universal signal then it
|
||||
* should leave it in place.
|
||||
*/
|
||||
sipQtSupport->qt_destroy_universal_slot(rx);
|
||||
|
||||
return PyBool_FromLong(res);
|
||||
}
|
||||
|
||||
/* Handle Python signals. Only PyQt3 will get this far. */
|
||||
assert(sipQtSupport->qt_disconnect_py_signal);
|
||||
|
||||
sipQtSupport->qt_disconnect_py_signal(txObj, sig, rxObj, slot);
|
||||
|
||||
Py_INCREF(Py_True);
|
||||
return Py_True;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Free the resources of a slot.
|
||||
*/
|
||||
void sip_api_free_sipslot(sipSlot *slot)
|
||||
{
|
||||
if (slot->name != NULL)
|
||||
{
|
||||
sip_api_free(slot->name);
|
||||
}
|
||||
else if (slot->weakSlot == Py_True)
|
||||
{
|
||||
Py_DECREF(slot->pyobj);
|
||||
}
|
||||
|
||||
/* Remove any weak reference. */
|
||||
Py_XDECREF(slot->weakSlot);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Implement strdup() using sip_api_malloc().
|
||||
*/
|
||||
static char *sipStrdup(const char *s)
|
||||
{
|
||||
char *d;
|
||||
|
||||
if ((d = (char *)sip_api_malloc(strlen(s) + 1)) != NULL)
|
||||
strcpy(d,s);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialise a slot, returning 0 if there was no error. If the signal was a
|
||||
* Qt signal, then the slot may be a Python signal or a Python slot. If the
|
||||
* signal was a Python signal, then the slot may be anything.
|
||||
*/
|
||||
int sip_api_save_slot(sipSlot *sp, PyObject *rxObj, const char *slot)
|
||||
{
|
||||
sp -> weakSlot = NULL;
|
||||
|
||||
if (slot == NULL)
|
||||
{
|
||||
sp -> name = NULL;
|
||||
|
||||
if (PyMethod_Check(rxObj))
|
||||
{
|
||||
/*
|
||||
* Python creates methods on the fly. We could increment the
|
||||
* reference count to keep it alive, but that would keep "self"
|
||||
* alive as well and would probably be a circular reference.
|
||||
* Instead we remember the component parts and hope they are still
|
||||
* valid when we re-create the method when we need it.
|
||||
*/
|
||||
sipSaveMethod(&sp -> meth,rxObj);
|
||||
|
||||
/* Notice if the class instance disappears. */
|
||||
sp -> weakSlot = getWeakRef(sp -> meth.mself);
|
||||
|
||||
/* This acts a flag to say that the slot is a method. */
|
||||
sp -> pyobj = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
PyObject *self;
|
||||
|
||||
/*
|
||||
* We know that it is another type of callable, ie. a
|
||||
* function/builtin.
|
||||
*/
|
||||
|
||||
if (PyCFunction_Check(rxObj) &&
|
||||
(self = PyCFunction_GET_SELF(rxObj)) != NULL &&
|
||||
PyObject_TypeCheck(self, (PyTypeObject *)&sipSimpleWrapper_Type))
|
||||
{
|
||||
/*
|
||||
* It is a wrapped C++ class method. We can't keep a copy
|
||||
* because they are generated on the fly and we can't take a
|
||||
* reference as that may keep the instance (ie. self) alive.
|
||||
* We therefore treat it as if the user had specified the slot
|
||||
* at "obj, SLOT('meth()')" rather than "obj.meth" (see below).
|
||||
*/
|
||||
|
||||
const char *meth;
|
||||
|
||||
/* Get the method name. */
|
||||
meth = ((PyCFunctionObject *)rxObj) -> m_ml -> ml_name;
|
||||
|
||||
if ((sp -> name = (char *)sip_api_malloc(strlen(meth) + 2)) == NULL)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Copy the name and set the marker that it needs converting to
|
||||
* a built-in method.
|
||||
*/
|
||||
sp -> name[0] = '\0';
|
||||
strcpy(&sp -> name[1],meth);
|
||||
|
||||
sp -> pyobj = self;
|
||||
sp -> weakSlot = getWeakRef(self);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Give the slot an extra reference to keep it alive and
|
||||
* remember we have done so by treating weakSlot specially.
|
||||
*/
|
||||
Py_INCREF(rxObj);
|
||||
sp->pyobj = rxObj;
|
||||
|
||||
Py_INCREF(Py_True);
|
||||
sp->weakSlot = Py_True;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((sp -> name = sipStrdup(slot)) == NULL)
|
||||
return -1;
|
||||
else if (isQtSlot(slot))
|
||||
{
|
||||
/*
|
||||
* The user has decided to connect a Python signal to a Qt slot and
|
||||
* specified the slot as "obj, SLOT('meth()')" rather than "obj.meth".
|
||||
*/
|
||||
|
||||
char *tail;
|
||||
|
||||
/* Remove any arguments. */
|
||||
if ((tail = strchr(sp -> name,'(')) != NULL)
|
||||
*tail = '\0';
|
||||
|
||||
/*
|
||||
* A bit of a hack to indicate that this needs converting to a built-in
|
||||
* method.
|
||||
*/
|
||||
sp -> name[0] = '\0';
|
||||
|
||||
/* Notice if the class instance disappears. */
|
||||
sp -> weakSlot = getWeakRef(rxObj);
|
||||
|
||||
sp -> pyobj = rxObj;
|
||||
}
|
||||
else
|
||||
/* It's a Qt signal. */
|
||||
sp -> pyobj = rxObj;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a weak reference to the given object.
|
||||
*/
|
||||
static PyObject *getWeakRef(PyObject *obj)
|
||||
{
|
||||
PyObject *wr;
|
||||
|
||||
if ((wr = PyWeakref_NewRef(obj,NULL)) == NULL)
|
||||
PyErr_Clear();
|
||||
|
||||
return wr;
|
||||
}
|
||||
1668
sip/siplib/sip.h
Normal file
1668
sip/siplib/sip.h
Normal file
File diff suppressed because it is too large
Load Diff
148
sip/siplib/sipint.h
Normal file
148
sip/siplib/sipint.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* This file defines the SIP library internal interfaces.
|
||||
*
|
||||
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
||||
*
|
||||
* This file is part of SIP.
|
||||
*
|
||||
* This copy of SIP is licensed for use under the terms of the SIP License
|
||||
* Agreement. See the file LICENSE for more details.
|
||||
*
|
||||
* This copy of SIP may also used under the terms of the GNU General Public
|
||||
* License v2 or v3 as published by the Free Software Foundation which can be
|
||||
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
||||
*
|
||||
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _SIPINT_H
|
||||
#define _SIPINT_H
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#undef TRUE
|
||||
#define TRUE 1
|
||||
|
||||
#undef FALSE
|
||||
#define FALSE 0
|
||||
|
||||
|
||||
/*
|
||||
* This defines a single entry in an object map's hash table.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
void *key; /* The C/C++ address. */
|
||||
sipSimpleWrapper *first; /* The first object at this address. */
|
||||
} sipHashEntry;
|
||||
|
||||
|
||||
/*
|
||||
* This defines the interface to a hash table class for mapping C/C++ addresses
|
||||
* to the corresponding wrapped Python object.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int primeIdx; /* Index into table sizes. */
|
||||
unsigned long size; /* Size of hash table. */
|
||||
unsigned long unused; /* Nr. unused in hash table. */
|
||||
unsigned long stale; /* Nr. stale in hash table. */
|
||||
sipHashEntry *hash_array; /* Current hash table. */
|
||||
} sipObjectMap;
|
||||
|
||||
|
||||
/*
|
||||
* Support for the descriptors.
|
||||
*/
|
||||
extern PyTypeObject sipMethodDescr_Type;
|
||||
PyObject *sipMethodDescr_New(PyMethodDef *pmd);
|
||||
|
||||
extern PyTypeObject sipVariableDescr_Type;
|
||||
PyObject *sipVariableDescr_New(sipVariableDef *vd, const sipTypeDef *td,
|
||||
const sipContainerDef *cod);
|
||||
|
||||
|
||||
/*
|
||||
* Support for API versions.
|
||||
*/
|
||||
PyObject *sipGetAPI(PyObject *self, PyObject *args);
|
||||
PyObject *sipSetAPI(PyObject *self, PyObject *args);
|
||||
int sip_api_is_api_enabled(const char *name, int from, int to);
|
||||
int sipIsRangeEnabled(sipExportedModuleDef *em, int range_index);
|
||||
int sipInitAPI(sipExportedModuleDef *em, PyObject *mod_dict);
|
||||
|
||||
|
||||
/*
|
||||
* Support for void pointers.
|
||||
*/
|
||||
extern PyTypeObject sipVoidPtr_Type;
|
||||
void *sip_api_convert_to_void_ptr(PyObject *obj);
|
||||
PyObject *sip_api_convert_from_void_ptr(void *val);
|
||||
PyObject *sip_api_convert_from_const_void_ptr(const void *val);
|
||||
PyObject *sip_api_convert_from_void_ptr_and_size(void *val, SIP_SSIZE_T size);
|
||||
PyObject *sip_api_convert_from_const_void_ptr_and_size(const void *val,
|
||||
SIP_SSIZE_T size);
|
||||
|
||||
|
||||
extern sipQtAPI *sipQtSupport; /* The Qt support API. */
|
||||
extern sipWrapperType sipSimpleWrapper_Type; /* The simple wrapper type. */
|
||||
extern sipTypeDef *sipQObjectType; /* The QObject type. */
|
||||
|
||||
void *sipGetRx(sipSimpleWrapper *txSelf, const char *sigargs, PyObject *rxObj,
|
||||
const char *slot, const char **memberp);
|
||||
PyObject *sip_api_connect_rx(PyObject *txObj, const char *sig, PyObject *rxObj,
|
||||
const char *slot, int type);
|
||||
PyObject *sip_api_disconnect_rx(PyObject *txObj, const char *sig,
|
||||
PyObject *rxObj,const char *slot);
|
||||
|
||||
|
||||
/*
|
||||
* These are part of the SIP API but are also used within the SIP module.
|
||||
*/
|
||||
void *sip_api_malloc(size_t nbytes);
|
||||
void sip_api_free(void *mem);
|
||||
void *sip_api_get_address(sipSimpleWrapper *w);
|
||||
void *sip_api_get_cpp_ptr(sipSimpleWrapper *w, const sipTypeDef *td);
|
||||
PyObject *sip_api_convert_from_type(void *cppPtr, const sipTypeDef *td,
|
||||
PyObject *transferObj);
|
||||
void sip_api_common_dtor(sipSimpleWrapper *sipSelf);
|
||||
void sip_api_start_thread(void);
|
||||
void sip_api_end_thread(void);
|
||||
void sip_api_free_sipslot(sipSlot *slot);
|
||||
int sip_api_same_slot(const sipSlot *sp, PyObject *rxObj, const char *slot);
|
||||
PyObject *sip_api_invoke_slot(const sipSlot *slot, PyObject *sigargs);
|
||||
void *sip_api_convert_rx(sipWrapper *txSelf, const char *sigargs,
|
||||
PyObject *rxObj, const char *slot, const char **memberp, int flags);
|
||||
int sip_api_save_slot(sipSlot *sp, PyObject *rxObj, const char *slot);
|
||||
|
||||
|
||||
/*
|
||||
* These are not part of the SIP API but are used within the SIP module.
|
||||
*/
|
||||
void sipSaveMethod(sipPyMethod *pm,PyObject *meth);
|
||||
void *sipGetPending(sipWrapper **op, int *fp);
|
||||
PyObject *sipWrapSimpleInstance(void *cppPtr, const sipTypeDef *td,
|
||||
sipWrapper *owner, int initflags);
|
||||
void *sipConvertRxEx(sipWrapper *txSelf, const char *sigargs,
|
||||
PyObject *rxObj, const char *slot, const char **memberp, int flags);
|
||||
|
||||
void sipOMInit(sipObjectMap *om);
|
||||
void sipOMFinalise(sipObjectMap *om);
|
||||
sipSimpleWrapper *sipOMFindObject(sipObjectMap *om, void *key,
|
||||
const sipTypeDef *td);
|
||||
void sipOMAddObject(sipObjectMap *om, sipSimpleWrapper *val);
|
||||
int sipOMRemoveObject(sipObjectMap *om, sipSimpleWrapper *val);
|
||||
|
||||
void sipSetBool(void *ptr,int val);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
10758
sip/siplib/siplib.c
Normal file
10758
sip/siplib/siplib.c
Normal file
File diff suppressed because it is too large
Load Diff
226
sip/siplib/threads.c
Normal file
226
sip/siplib/threads.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Thread support for the SIP library. This module provides the hooks for
|
||||
* C++ classes that provide a thread interface to interact properly with the
|
||||
* Python threading infrastructure.
|
||||
*
|
||||
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
|
||||
*
|
||||
* This file is part of SIP.
|
||||
*
|
||||
* This copy of SIP is licensed for use under the terms of the SIP License
|
||||
* Agreement. See the file LICENSE for more details.
|
||||
*
|
||||
* This copy of SIP may also used under the terms of the GNU General Public
|
||||
* License v2 or v3 as published by the Free Software Foundation which can be
|
||||
* found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package.
|
||||
*
|
||||
* SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*/
|
||||
|
||||
|
||||
#include "sip.h"
|
||||
#include "sipint.h"
|
||||
|
||||
|
||||
/*
|
||||
* The data associated with pending request to wrap an object.
|
||||
*/
|
||||
typedef struct _pendingDef {
|
||||
void *cpp; /* The C/C++ object ot be wrapped. */
|
||||
sipWrapper *owner; /* The owner of the object. */
|
||||
int flags; /* The flags. */
|
||||
} pendingDef;
|
||||
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
|
||||
#include <pythread.h>
|
||||
|
||||
|
||||
/*
|
||||
* The per thread data we need to maintain.
|
||||
*/
|
||||
typedef struct _threadDef {
|
||||
long thr_ident; /* The thread identifier. */
|
||||
pendingDef pending; /* An object waiting to be wrapped. */
|
||||
struct _threadDef *next; /* Next in the list. */
|
||||
} threadDef;
|
||||
|
||||
|
||||
static threadDef *threads = NULL; /* Linked list of threads. */
|
||||
|
||||
|
||||
static threadDef *currentThreadDef(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static pendingDef pending; /* An object waiting to be wrapped. */
|
||||
|
||||
|
||||
/*
|
||||
* Get the address of any C/C++ object waiting to be wrapped.
|
||||
*/
|
||||
void *sipGetPending(sipWrapper **op, int *fp)
|
||||
{
|
||||
pendingDef *pp;
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
threadDef *thread;
|
||||
|
||||
if ((thread = currentThreadDef()) != NULL)
|
||||
pp = &thread->pending;
|
||||
else
|
||||
pp = &pending;
|
||||
#else
|
||||
pp = &pending;
|
||||
#endif
|
||||
|
||||
if (pp->cpp != NULL)
|
||||
{
|
||||
if (op != NULL)
|
||||
*op = pp->owner;
|
||||
|
||||
if (fp != NULL)
|
||||
*fp = pp->flags;
|
||||
}
|
||||
|
||||
return pp->cpp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert a new C/C++ pointer to a Python instance.
|
||||
*/
|
||||
PyObject *sipWrapSimpleInstance(void *cppPtr, const sipTypeDef *td,
|
||||
sipWrapper *owner, int flags)
|
||||
{
|
||||
static PyObject *nullargs = NULL;
|
||||
|
||||
pendingDef old_pending;
|
||||
PyObject *self;
|
||||
#ifdef WITH_THREAD
|
||||
threadDef *thread;
|
||||
#endif
|
||||
|
||||
if (nullargs == NULL && (nullargs = PyTuple_New(0)) == NULL)
|
||||
return NULL;
|
||||
|
||||
if (cppPtr == NULL)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
/*
|
||||
* Object creation can trigger the Python garbage collector which in turn
|
||||
* can execute arbitrary Python code which can then call this function
|
||||
* recursively. Therefore we save any existing pending object before
|
||||
* setting the new one.
|
||||
*/
|
||||
#ifdef WITH_THREAD
|
||||
if ((thread = currentThreadDef()) != NULL)
|
||||
{
|
||||
old_pending = thread->pending;
|
||||
|
||||
thread->pending.cpp = cppPtr;
|
||||
thread->pending.owner = owner;
|
||||
thread->pending.flags = flags;
|
||||
}
|
||||
else
|
||||
{
|
||||
old_pending = pending;
|
||||
|
||||
pending.cpp = cppPtr;
|
||||
pending.owner = owner;
|
||||
pending.flags = flags;
|
||||
}
|
||||
#else
|
||||
old_pending = pending;
|
||||
|
||||
pending.cpp = cppPtr;
|
||||
pending.owner = owner;
|
||||
pending.flags = flags;
|
||||
#endif
|
||||
|
||||
self = PyObject_Call((PyObject *)sipTypeAsPyTypeObject(td), nullargs, NULL);
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
if (thread != NULL)
|
||||
thread->pending = old_pending;
|
||||
else
|
||||
pending = old_pending;
|
||||
#else
|
||||
pending = old_pending;
|
||||
#endif
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is called from a newly created thread to initialise some thread local
|
||||
* storage.
|
||||
*/
|
||||
void sip_api_start_thread(void)
|
||||
{
|
||||
#ifdef WITH_THREAD
|
||||
threadDef *thread;
|
||||
|
||||
/* Save the thread ID. First, find an empty slot in the list. */
|
||||
for (thread = threads; thread != NULL; thread = thread->next)
|
||||
if (thread->thr_ident == 0)
|
||||
break;
|
||||
|
||||
if (thread == NULL)
|
||||
{
|
||||
thread = sip_api_malloc(sizeof (threadDef));
|
||||
thread->next = threads;
|
||||
threads = thread;
|
||||
}
|
||||
|
||||
if (thread != NULL)
|
||||
{
|
||||
thread->thr_ident = PyThread_get_thread_ident();
|
||||
thread->pending.cpp = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle the termination of a thread. The thread state should already have
|
||||
* been handled by the last call to PyGILState_Release().
|
||||
*/
|
||||
void sip_api_end_thread(void)
|
||||
{
|
||||
#ifdef WITH_THREAD
|
||||
threadDef *thread;
|
||||
|
||||
/* We have the GIL at this point. */
|
||||
if ((thread = currentThreadDef()) != NULL)
|
||||
thread->thr_ident = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef WITH_THREAD
|
||||
|
||||
/*
|
||||
* Return the thread data for the current thread or NULL if it wasn't
|
||||
* recognised.
|
||||
*/
|
||||
static threadDef *currentThreadDef(void)
|
||||
{
|
||||
threadDef *thread;
|
||||
long ident = PyThread_get_thread_ident();
|
||||
|
||||
for (thread = threads; thread != NULL; thread = thread->next)
|
||||
if (thread->thr_ident == ident)
|
||||
break;
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
#endif
|
||||
1017
sip/siplib/voidptr.c
Normal file
1017
sip/siplib/voidptr.c
Normal file
File diff suppressed because it is too large
Load Diff
3
src/README.txt
Normal file
3
src/README.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
This directory holds source code that is not generated by any of the tools,
|
||||
but is edited by hand instead. It may include C/C++, SIP or Python code as
|
||||
needed.
|
||||
3
src/__init__.py
Normal file
3
src/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
from _core import *
|
||||
31
src/string.sip
Normal file
31
src/string.sip
Normal file
@@ -0,0 +1,31 @@
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// Name: string.sip
|
||||
// Purpose: Implements a %MappedType for wxString
|
||||
//
|
||||
// Author: Robin Dunn
|
||||
//
|
||||
// Created: 9-Nov-2010
|
||||
// RCS-ID: $Id:$
|
||||
// Copyright: (c) 2010 by Total Control Software
|
||||
// Licence: wxWindows license
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// We don't want the Python user to ever need to deal with wxString, so it
|
||||
// will be mapped to and from Python UNicode objects using the code snippets
|
||||
// below.
|
||||
|
||||
%MappedType wxString
|
||||
{
|
||||
// Code to test a PyObject for compatibility and to convert from a
|
||||
// PyObject to a wxString
|
||||
%ConvertToTypeCode
|
||||
// TODO
|
||||
%End
|
||||
|
||||
// Code to convert a wxString to a PyObject
|
||||
%ConvertFromTypeCode
|
||||
// TODO
|
||||
%End
|
||||
|
||||
};
|
||||
4
src/wxc.rc
Normal file
4
src/wxc.rc
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "wx/msw/wx.rc"
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user