summaryrefslogtreecommitdiffstats
path: root/python/sip/siputils.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/sip/siputils.py')
-rw-r--r--python/sip/siputils.py2354
1 files changed, 2354 insertions, 0 deletions
diff --git a/python/sip/siputils.py b/python/sip/siputils.py
new file mode 100644
index 00000000..d4e596ca
--- /dev/null
+++ b/python/sip/siputils.py
@@ -0,0 +1,2354 @@
+# This module is intended to be used by the build/installation scripts of
+# extension modules created with SIP. It provides information about file
+# locations, version numbers etc., and provides some classes and functions.
+#
+# Copyright (c) 2007
+# Riverbank Computing Limited <info@riverbankcomputing.co.uk>
+#
+# 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.
+#
+# SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+
+import sys
+import os
+import string
+import types
+import stat
+import re
+
+
+# These are installation specific values created when SIP was configured.
+# @SIP_CONFIGURATION@
+
+# The stack of configuration dictionaries.
+_config_stack = []
+
+
+class Configuration(object):
+ """The class that represents SIP configuration values.
+ """
+ def __init__(self, sub_cfg=None):
+ """Initialise an instance of the class.
+
+ sub_cfg is the list of sub-class configurations. It should be None
+ when called normally.
+ """
+ # Find the build macros in the closest imported module from where this
+ # was originally defined.
+ self._macros = None
+
+ for cls in self.__class__.__mro__:
+ if cls is object:
+ continue
+
+ mod = sys.modules[cls.__module__]
+
+ if hasattr(mod, "_default_macros"):
+ self._macros = mod._default_macros
+ break
+
+ if sub_cfg:
+ cfg = sub_cfg
+ else:
+ cfg = []
+
+ cfg.append(_pkg_config)
+
+ global _config_stack
+ _config_stack = cfg
+
+ def __getattr__(self, name):
+ """Allow configuration values and user options to be handled as
+ instance variables.
+
+ name is the name of the configuration value or user option.
+ """
+ for cfg in _config_stack:
+ try:
+ return cfg[name]
+ except KeyError:
+ pass
+
+ raise AttributeError, "\"%s\" is not a valid configuration value or user option" % name
+
+ def build_macros(self):
+ """Return the dictionary of platform specific build macros.
+ """
+ return self._macros
+
+ def set_build_macros(self, macros):
+ """Set the dictionary of build macros to be use when generating
+ Makefiles.
+
+ macros is the dictionary of platform specific build macros.
+ """
+ self._macros = macros
+
+
+class _UniqueList:
+ """A limited list that ensures all its elements are unique.
+ """
+ def __init__(self, value=None):
+ """Initialise the instance.
+
+ value is the initial value of the list.
+ """
+ if value is None:
+ self._list = []
+ else:
+ self._list = value
+
+ def append(self, value):
+ """Append a value to the list if it isn't already present.
+
+ value is the value to append.
+ """
+ if value not in self._list:
+ self._list.append(value)
+
+ def lextend(self, value):
+ """A normal list extend ignoring the uniqueness.
+
+ value is the list of elements to append.
+ """
+ self._list.extend(value)
+
+ def extend(self, value):
+ """Append each element of a value to a list if it isn't already
+ present.
+
+ value is the list of elements to append.
+ """
+ for el in value:
+ self.append(el)
+
+ def as_list(self):
+ """Return the list as a raw list.
+ """
+ return self._list
+
+
+class _Macro:
+ """A macro that can be manipulated as a list.
+ """
+ def __init__(self, name, value):
+ """Initialise the instance.
+
+ name is the name of the macro.
+ value is the initial value of the macro.
+ """
+ self._name = name
+ self.set(value)
+
+ def set(self, value):
+ """Explicitly set the value of the macro.
+
+ value is the new value. It may be a string, a list of strings or a
+ _UniqueList instance.
+ """
+ self._macro = []
+
+ if isinstance(value, _UniqueList):
+ value = value.as_list()
+
+ if type(value) == types.ListType:
+ self.extend(value)
+ else:
+ self.append(value)
+
+ def append(self, value):
+ """Append a value to the macro.
+
+ value is the value to append.
+ """
+ if value:
+ self._macro.append(value)
+
+ def extend(self, value):
+ """Append each element of a value to the macro.
+
+ value is the list of elements to append.
+ """
+ for el in value:
+ self.append(el)
+
+ def as_list(self):
+ """Return the macro as a list.
+ """
+ return self._macro
+
+
+class Makefile:
+ """The base class for the different types of Makefiles.
+ """
+ def __init__(self, configuration, console=0, qt=0, opengl=0, python=0,
+ threaded=0, warnings=1, debug=0, dir=None,
+ makefile="Makefile", installs=None, universal=''):
+ """Initialise an instance of the target. All the macros are left
+ unchanged allowing scripts to manipulate them at will.
+
+ configuration is the current configuration.
+ console is set if the target is a console (rather than windows) target.
+ qt is set if the target uses Qt. For Qt v4 a list of Qt libraries may
+ be specified and a simple non-zero value implies QtCore and QtGui.
+ opengl is set if the target uses OpenGL.
+ python is set if the target #includes Python.h.
+ debug is set to generated a debugging version of the target.
+ threaded is set if the target requires thread support. It is
+ automatically set if the target uses Qt and Qt has thread support
+ enabled.
+ warnings is set if compiler warning messages are required.
+ debug is set if debugging symbols should be generated.
+ dir is the directory for build files and Makefiles.
+ makefile is the name of the Makefile.
+ installs is a list of extra install targets. Each element is a two
+ part list, the first of which is the source and the second is the
+ destination. If the source is another list then it is a set of source
+ files and the destination is a directory.
+ universal is the name of the SDK if the target is a MacOS/X universal
+ binary.
+ """
+ if qt:
+ if not hasattr(configuration, "qt_version"):
+ error("The target uses Qt but pyqtconfig has not been imported.")
+
+ # For Qt v4 interpret Qt support as meaning link against the core
+ # and GUI libraries (which corresponds to the default qmake
+ # configuration). Also allow a list of Qt v4 modules to be
+ # specified.
+ if configuration.qt_version >= 0x040000:
+ if type(qt) != types.ListType:
+ qt = ["QtCore", "QtGui"]
+
+ self._threaded = configuration.qt_threaded
+ else:
+ self._threaded = threaded
+
+ if sys.platform != "darwin":
+ universal = ''
+
+ self.config = configuration
+ self.console = console
+ self._qt = qt
+ self._opengl = opengl
+ self._python = python
+ self._warnings = warnings
+ self._debug = debug
+ self._dir = dir
+ self._makefile = makefile
+ self._installs = installs
+ self._universal = universal
+
+ self._finalised = 0
+
+ # Copy the macros and convert them all to instance lists.
+ macros = configuration.build_macros()
+
+ for m in macros.keys():
+ # Allow the user to override the default.
+ try:
+ val = getattr(configuration, m)
+ except AttributeError:
+ val = macros[m]
+
+ # These require special handling as they are (potentially) a set of
+ # space separated values rather than a single value that might
+ # contain spaces.
+ if m in ("DEFINES", "CONFIG") or m[:6] in ("INCDIR", "LIBDIR"):
+ val = string.split(val)
+
+ # We also want to treat lists of libraries in the same way so that
+ # duplicates get eliminated.
+ if m[:4] == "LIBS":
+ val = string.split(val)
+
+ self.__dict__[m] = _Macro(m, val)
+
+ # This is used to alter the configuration more significantly than can
+ # be done with just configuration files.
+ self.generator = self.optional_string("MAKEFILE_GENERATOR", "UNIX")
+
+ # These are what configuration scripts normally only need to change.
+ self.extra_cflags = []
+ self.extra_cxxflags = []
+ self.extra_defines = []
+ self.extra_include_dirs = []
+ self.extra_lflags = []
+ self.extra_lib_dirs = []
+ self.extra_libs = []
+
+ # Get these once and make them available to sub-classes.
+ if sys.platform == "win32":
+ def_copy = "copy"
+ def_rm = "del"
+ def_mkdir = "mkdir"
+ def_chk_dir_exists = "if not exist"
+ else:
+ def_copy = "cp -f"
+ def_rm = "rm -f"
+ def_mkdir = "mkdir -p"
+ def_chk_dir_exists = "test -d"
+
+ self.copy = self.optional_string("COPY", def_copy)
+ self.rm = self.optional_string("DEL_FILE", def_rm)
+ self.mkdir = self.optional_string("MKDIR", def_mkdir)
+ self.chkdir = self.optional_string("CHK_DIR_EXISTS", def_chk_dir_exists)
+
+
+ def finalise(self):
+ """Finalise the macros by doing any consolidation that isn't specific
+ to a Makefile.
+ """
+ # Extract the things we might need from the Windows Qt configuration.
+ if self._qt:
+ wcfg = string.split(self.config.qt_winconfig)
+ win_shared = ("shared" in wcfg)
+ win_exceptions = ("exceptions" in wcfg)
+ win_rtti = ("rtti" in wcfg)
+ win_stl = ("stl" in wcfg)
+ else:
+ win_shared = 1
+ win_exceptions = 0
+ win_rtti = 0
+ win_stl = 0
+
+ # Get what we are going to transform.
+ cflags = _UniqueList()
+ cflags.extend(self.extra_cflags)
+ cflags.extend(self.optional_list("CFLAGS"))
+
+ cxxflags = _UniqueList()
+ cxxflags.extend(self.extra_cxxflags)
+ cxxflags.extend(self.optional_list("CXXFLAGS"))
+
+ defines = _UniqueList()
+ defines.extend(self.extra_defines)
+ defines.extend(self.optional_list("DEFINES"))
+
+ incdir = _UniqueList(["."])
+ incdir.extend(self.extra_include_dirs)
+ incdir.extend(self.optional_list("INCDIR"))
+
+ lflags = _UniqueList()
+ lflags.extend(self.extra_lflags)
+ lflags.extend(self.optional_list("LFLAGS"))
+
+ libdir = _UniqueList()
+ libdir.extend(self.extra_lib_dirs)
+ libdir.extend(self.optional_list("LIBDIR"))
+
+ # Handle MacOS/X universal binaries.
+ if self._universal:
+ unicflags = ('-arch ppc -arch i386 -isysroot %s' % self._universal).split()
+ unilflags = ('-arch ppc -arch i386 -Wl,-syslibroot,%s' % self._universal).split()
+
+ cflags.lextend(unicflags)
+ cxxflags.lextend(unicflags)
+ lflags.lextend(unilflags)
+
+ # Don't use a unique list as libraries may need to be searched more
+ # than once. Also MacOS/X uses the form "-framework lib" so we don't
+ # want to lose the multiple "-framework".
+ libs = []
+
+ for l in self.extra_libs:
+ libs.append(self.platform_lib(l))
+
+ if self._qt:
+ libs.extend(self._dependent_libs(l))
+
+ libs.extend(self.optional_list("LIBS"))
+
+ rpaths = _UniqueList()
+
+ for l in self.extra_lib_dirs:
+ # Ignore relative directories. This is really a hack to handle
+ # SIP v3 inter-module linking.
+ if os.path.dirname(l) not in ("", ".", ".."):
+ rpaths.append(l)
+
+ if self._python:
+ incdir.append(self.config.py_inc_dir)
+ incdir.append(self.config.py_conf_inc_dir)
+
+ if sys.platform == "cygwin":
+ libdir.append(self.config.py_lib_dir)
+
+ py_lib = "python%u.%u" % ((self.config.py_version >> 16), ((self.config.py_version >> 8) & 0xff))
+ libs.append(self.platform_lib(py_lib))
+ elif sys.platform == "win32":
+ libdir.append(self.config.py_lib_dir)
+
+ py_lib = "python%u%u" % ((self.config.py_version >> 16), ((self.config.py_version >> 8) & 0xff))
+
+ # For Borland use the OMF version of the Python library if it
+ # exists, otherwise assume that Python was built with Borland
+ # and use the normal library.
+ if self.generator == "BMAKE":
+ bpy_lib = py_lib + "_bcpp"
+ bpy_lib_path = os.path.join(self.config.py_lib_dir, self.platform_lib(bpy_lib))
+
+ if os.access(bpy_lib_path, os.F_OK):
+ py_lib = bpy_lib
+
+ if self._debug:
+ py_lib = py_lib + "_d"
+
+ if self.generator != "MINGW":
+ cflags.append("/D_DEBUG")
+ cxxflags.append("/D_DEBUG")
+
+ libs.append(self.platform_lib(py_lib))
+
+ if self.generator in ("MSVC", "MSVC.NET", "BMAKE"):
+ if win_exceptions:
+ cflags_exceptions = "CFLAGS_EXCEPTIONS_ON"
+ cxxflags_exceptions = "CXXFLAGS_EXCEPTIONS_ON"
+ else:
+ cflags_exceptions = "CFLAGS_EXCEPTIONS_OFF"
+ cxxflags_exceptions = "CXXFLAGS_EXCEPTIONS_OFF"
+
+ cflags.extend(self.optional_list(cflags_exceptions))
+ cxxflags.extend(self.optional_list(cxxflags_exceptions))
+
+ if win_rtti:
+ cflags_rtti = "CFLAGS_RTTI_ON"
+ cxxflags_rtti = "CXXFLAGS_RTTI_ON"
+ else:
+ cflags_rtti = "CFLAGS_RTTI_OFF"
+ cxxflags_rtti = "CXXFLAGS_RTTI_OFF"
+
+ cflags.extend(self.optional_list(cflags_rtti))
+ cxxflags.extend(self.optional_list(cxxflags_rtti))
+
+ if win_stl:
+ cflags_stl = "CFLAGS_STL_ON"
+ cxxflags_stl = "CXXFLAGS_STL_ON"
+ else:
+ cflags_stl = "CFLAGS_STL_OFF"
+ cxxflags_stl = "CXXFLAGS_STL_OFF"
+
+ cflags.extend(self.optional_list(cflags_stl))
+ cxxflags.extend(self.optional_list(cxxflags_stl))
+
+ if self._debug:
+ if win_shared:
+ cflags_mt = "CFLAGS_MT_DLLDBG"
+ cxxflags_mt = "CXXFLAGS_MT_DLLDBG"
+ else:
+ cflags_mt = "CFLAGS_MT_DBG"
+ cxxflags_mt = "CXXFLAGS_MT_DBG"
+
+ cflags_debug = "CFLAGS_DEBUG"
+ cxxflags_debug = "CXXFLAGS_DEBUG"
+ lflags_debug = "LFLAGS_DEBUG"
+ else:
+ if win_shared:
+ cflags_mt = "CFLAGS_MT_DLL"
+ cxxflags_mt = "CXXFLAGS_MT_DLL"
+ else:
+ cflags_mt = "CFLAGS_MT"
+ cxxflags_mt = "CXXFLAGS_MT"
+
+ cflags_debug = "CFLAGS_RELEASE"
+ cxxflags_debug = "CXXFLAGS_RELEASE"
+ lflags_debug = "LFLAGS_RELEASE"
+
+ if self.generator in ("MSVC", "MSVC.NET", "BMAKE"):
+ if self._threaded:
+ cflags.extend(self.optional_list(cflags_mt))
+ cxxflags.extend(self.optional_list(cxxflags_mt))
+
+ if self.console:
+ cflags.extend(self.optional_list("CFLAGS_CONSOLE"))
+ cxxflags.extend(self.optional_list("CXXFLAGS_CONSOLE"))
+
+ cflags.extend(self.optional_list(cflags_debug))
+ cxxflags.extend(self.optional_list(cxxflags_debug))
+ lflags.extend(self.optional_list(lflags_debug))
+
+ if self._warnings:
+ cflags_warn = "CFLAGS_WARN_ON"
+ cxxflags_warn = "CXXFLAGS_WARN_ON"
+ else:
+ cflags_warn = "CFLAGS_WARN_OFF"
+ cxxflags_warn = "CXXFLAGS_WARN_OFF"
+
+ cflags.extend(self.optional_list(cflags_warn))
+ cxxflags.extend(self.optional_list(cxxflags_warn))
+
+ if self._threaded:
+ cflags.extend(self.optional_list("CFLAGS_THREAD"))
+ cxxflags.extend(self.optional_list("CXXFLAGS_THREAD"))
+ lflags.extend(self.optional_list("LFLAGS_THREAD"))
+
+ if self._qt:
+ if self.generator != "UNIX" and win_shared:
+ defines.append("QT_DLL")
+
+ if not self._debug:
+ defines.append("QT_NO_DEBUG")
+
+ if self.config.qt_version >= 0x040000:
+ for mod in self._qt:
+ if mod == "QtCore":
+ defines.append("QT_CORE_LIB")
+ elif mod == "QtGui":
+ defines.append("QT_GUI_LIB")
+ elif mod == "QtNetwork":
+ defines.append("QT_NETWORK_LIB")
+ elif mod == "QtOpenGL":
+ defines.append("QT_OPENGL_LIB")
+ elif mod == "QtSql":
+ defines.append("QT_SQL_LIB")
+ elif mod == "QtTest":
+ defines.append("QT_TEST_LIB")
+ elif mod == "QtXml":
+ defines.append("QT_XML_LIB")
+ elif self._threaded:
+ defines.append("QT_THREAD_SUPPORT")
+
+ # Handle library directories.
+ libdir_qt = self.optional_list("LIBDIR_QT")
+ libdir.extend(libdir_qt)
+ rpaths.extend(libdir_qt)
+
+ if self.config.qt_version >= 0x040000:
+ # For Windows: the macros that define the dependencies on
+ # Windows libraries.
+ wdepmap = {
+ "QtCore": "LIBS_CORE",
+ "QtGui": "LIBS_GUI",
+ "QtNetwork": "LIBS_NETWORK",
+ "QtOpenGL": "LIBS_OPENGL"
+ }
+
+ # For Windows: the dependencies between Qt libraries.
+ qdepmap = {
+ "QtAssistant": ("QtCore", "QtGui", "QtNetwork"),
+ "QtGui": ("QtCore", ),
+ "QtNetwork": ("QtCore", ),
+ "QtOpenGL": ("QtCore", "QtGui"),
+ "QtSql": ("QtCore", ),
+ "QtSvg": ("QtCore", "QtGui", "QtXml"),
+ "QtTest": ("QtCore", "QtGui"),
+ "QtXml": ("QtCore", ),
+ "QtDesigner": ("QtCore", "QtGui"),
+ "QAxContainer": ("QtCore", "QtGui")
+ }
+
+ # The QtSql .prl file doesn't include QtGui as a dependency (at
+ # least on Linux) so we explcitly set the dependency here for
+ # everything.
+ if "QtSql" in self._qt:
+ if "QtGui" not in self._qt:
+ self._qt.append("QtGui")
+
+ # With Qt v4.2.0, the QtAssistantClient library is now a shared
+ # library on UNIX. The QtAssistantClient .prl file doesn't
+ # include QtGui and QtNetwork as a dependency any longer. This
+ # seems to be a bug in Qt v4.2.0. We explicitly set the
+ # dependencies here.
+ if self.config.qt_version >= 0x040200 and "QtAssistant" in self._qt:
+ if "QtGui" not in self._qt:
+ self._qt.append("QtGui")
+ if "QtNetwork" not in self._qt:
+ self._qt.append("QtNetwork")
+
+ for mod in self._qt:
+ lib = self._qt4_module_to_lib(mod)
+ libs.append(self.platform_lib(lib, self._is_framework(mod)))
+
+ if sys.platform == "win32":
+ # On Windows the dependent libraries seem to be in
+ # qmake.conf rather than the .prl file and the
+ # inter-dependencies between Qt libraries don't seem to
+ # be anywhere.
+ deps = _UniqueList()
+
+ if mod in wdepmap.keys():
+ deps.extend(self.optional_list(wdepmap[mod]))
+
+ if mod in qdepmap.keys():
+ for qdep in qdepmap[mod]:
+ # Ignore the dependency if it is explicitly
+ # linked.
+ if qdep not in self._qt:
+ libs.append(self.platform_lib(self._qt4_module_to_lib(qdep)))
+
+ if qdep in wdepmap.keys():
+ deps.extend(self.optional_list(wdepmap[qdep]))
+
+ libs.extend(deps.as_list())
+ else:
+ libs.extend(self._dependent_libs(lib, self._is_framework(mod)))
+ else:
+ # Windows needs the version number appended if Qt is a DLL.
+ qt_lib = self.config.qt_lib
+
+ if self.generator in ("MSVC", "MSVC.NET", "BMAKE") and win_shared:
+ qt_lib = qt_lib + string.replace(version_to_string(self.config.qt_version), ".", "")
+
+ if self.config.qt_edition == "non-commercial":
+ qt_lib = qt_lib + "nc"
+
+ libs.append(self.platform_lib(qt_lib, self.config.qt_framework))
+ libs.extend(self._dependent_libs(self.config.qt_lib))
+
+ # Handle header directories.
+ try:
+ specd_base = self.config.qt_data_dir
+ except AttributeError:
+ specd_base = self.config.qt_dir
+
+ specd = os.path.join(specd_base, "mkspecs", "default")
+
+ if not os.access(specd, os.F_OK):
+ specd = os.path.join(specd_base, "mkspecs", self.config.platform)
+
+ incdir.append(specd)
+
+ qtincdir = self.optional_list("INCDIR_QT")
+
+ if qtincdir:
+ if self.config.qt_version >= 0x040000:
+ for mod in self._qt:
+ if mod == "QAxContainer":
+ incdir.append(os.path.join(qtincdir[0], "ActiveQt"))
+ elif self._is_framework(mod):
+ if mod == "QtAssistant" and self.config.qt_version < 0x040202:
+ mod = "QtAssistantClient"
+
+ incdir.append(os.path.join(libdir_qt[0], mod + ".framework", "Headers"))
+ else:
+ incdir.append(os.path.join(qtincdir[0], mod))
+
+ # This must go after the module include directories.
+ incdir.extend(qtincdir)
+
+ if self._opengl:
+ incdir.extend(self.optional_list("INCDIR_OPENGL"))
+ lflags.extend(self.optional_list("LFLAGS_OPENGL"))
+ libdir.extend(self.optional_list("LIBDIR_OPENGL"))
+ libs.extend(self.optional_list("LIBS_OPENGL"))
+
+ if self._qt or self._opengl:
+ incdir.extend(self.optional_list("INCDIR_X11"))
+ libdir.extend(self.optional_list("LIBDIR_X11"))
+ libs.extend(self.optional_list("LIBS_X11"))
+
+ if self._threaded:
+ libs.extend(self.optional_list("LIBS_THREAD"))
+ libs.extend(self.optional_list("LIBS_RTMT"))
+ else:
+ libs.extend(self.optional_list("LIBS_RT"))
+
+ if self.console:
+ libs.extend(self.optional_list("LIBS_CONSOLE"))
+
+ libs.extend(self.optional_list("LIBS_WINDOWS"))
+
+ lflags.extend(self._platform_rpaths(rpaths.as_list()))
+
+ # Save the transformed values.
+ self.CFLAGS.set(cflags)
+ self.CXXFLAGS.set(cxxflags)
+ self.DEFINES.set(defines)
+ self.INCDIR.set(incdir)
+ self.LFLAGS.set(lflags)
+ self.LIBDIR.set(libdir)
+ self.LIBS.set(libs)
+
+ # Don't do it again because it has side effects.
+ self._finalised = 1
+
+ def _is_framework(self, mod):
+ """Return true if the given Qt module is a framework.
+ """
+ return (self.config.qt_framework and (self.config.qt_version >= 0x040200 or mod != "QtAssistant"))
+
+ def _qt4_module_to_lib(self, mname):
+ """Return the name of the Qt4 library corresponding to a module.
+
+ mname is the name of the module.
+ """
+ if mname == "QtAssistant":
+ if self.config.qt_version >= 0x040202 and sys.platform == "darwin":
+ lib = mname
+ else:
+ lib = "QtAssistantClient"
+ else:
+ lib = mname
+
+ if self._debug:
+ if sys.platform == "win32":
+ lib = lib + "d"
+ elif self.config.qt_version < 0x040200 or sys.platform == "darwin":
+ lib = lib + "_debug"
+
+ if sys.platform == "win32":
+ if (mname in ("QtCore", "QtGui", "QtNetwork", "QtOpenGL",
+ "QtSql", "QtSvg", "QtTest", "QtXml", "QtDesigner") or
+ (self.config.qt_version >= 0x040200 and mname == "QtAssistant")):
+ lib = lib + "4"
+
+ return lib
+
+ def optional_list(self, name):
+ """Return an optional Makefile macro as a list.
+
+ name is the name of the macro.
+ """
+ return self.__dict__[name].as_list()
+
+ def optional_string(self, name, default=""):
+ """Return an optional Makefile macro as a string.
+
+ name is the name of the macro.
+ default is the default value
+ """
+ s = string.join(self.optional_list(name))
+
+ if not s:
+ s = default
+
+ return s
+
+ def required_string(self, name):
+ """Return a required Makefile macro as a string.
+
+ name is the name of the macro.
+ """
+ s = self.optional_string(name)
+
+ if not s:
+ raise ValueError, "\"%s\" must have a non-empty value" % name
+
+ return s
+
+ def _platform_rpaths(self, rpaths):
+ """Return a list of platform specific rpath flags.
+
+ rpaths is the cannonical list of rpaths.
+ """
+ flags = []
+ prefix = self.optional_string("RPATH")
+
+ if prefix:
+ for r in rpaths:
+ flags.append(_quote(prefix + r))
+
+ return flags
+
+ def platform_lib(self, clib, framework=0):
+ """Return a library name in platform specific form.
+
+ clib is the library name in cannonical form.
+ framework is set of the library is implemented as a MacOS framework.
+ """
+ if self.generator in ("MSVC", "MSVC.NET", "BMAKE"):
+ plib = clib + ".lib"
+ elif sys.platform == "darwin" and framework:
+ plib = "-framework " + clib
+ else:
+ plib = "-l" + clib
+
+ return plib
+
+ def _dependent_libs(self, clib, framework=0):
+ """Return a list of additional libraries (in platform specific form)
+ that must be linked with a library.
+
+ clib is the library name in cannonical form.
+ framework is set of the library is implemented as a MacOS framework.
+ """
+ prl_libs = []
+
+ if self.generator in ("MSVC", "MSVC.NET", "BMAKE"):
+ prl_name = os.path.join(self.config.qt_lib_dir, clib + ".prl")
+ elif sys.platform == "darwin" and framework:
+ prl_name = os.path.join(self.config.qt_lib_dir, clib + ".framework", clib + ".prl")
+ else:
+ prl_name = os.path.join(self.config.qt_lib_dir, "lib" + clib + ".prl")
+
+ if os.access(prl_name, os.F_OK):
+ try:
+ f = open(prl_name, "r")
+ except IOError, detail:
+ error("Unable to open \"%s\": %s" % (prl_name, detail))
+
+ line = f.readline()
+ while line:
+ line = string.strip(line)
+ if line and line[0] != "#":
+ eq = string.find(line, "=")
+ if eq > 0 and string.strip(line[:eq]) == "QMAKE_PRL_LIBS":
+ prl_libs = string.split(line[eq + 1:])
+ break
+
+ line = f.readline()
+
+ f.close()
+
+ return prl_libs
+
+
+ def parse_build_file(self, filename):
+ """
+ Parse a build file and return the corresponding dictionary.
+
+ filename is the name of the build file. If it is a dictionary instead
+ then its contents are validated.
+ """
+ if type(filename) is types.DictType:
+ bfname = "dictionary"
+ dict = filename
+ else:
+ if self._dir:
+ bfname = os.path.join(self._dir, filename)
+ else:
+ bfname = filename
+
+ dict = {}
+
+ try:
+ f = open(bfname, "r")
+ except IOError, detail:
+ error("Unable to open \"%s\": %s" % (bfname, detail))
+
+ line_nr = 1
+ line = f.readline()
+
+ while line:
+ line = string.strip(line)
+
+ if line and line[0] != "#":
+ eq = string.find(line, "=")
+
+ if eq <= 0:
+ error("\"%s\" line %d: Line must be in the form 'name = value value...'." % (bfname, line_nr))
+
+ dict[string.strip(line[:eq])] = string.strip(line[eq + 1:])
+
+ line_nr = line_nr + 1
+ line = f.readline()
+
+ f.close()
+
+ # Check the compulsory values.
+ for i in ("target", "sources"):
+ try:
+ dict[i]
+ except KeyError:
+ error("\"%s\" is missing from \"%s\"." % (i, bfname))
+
+ # Get the optional values.
+ for i in ("headers", "moc_headers"):
+ try:
+ dict[i]
+ except KeyError:
+ dict[i] = ""
+
+ # Generate the list of objects.
+ if self.generator in ("MSVC", "MSVC.NET", "BMAKE"):
+ ext = ".obj"
+ else:
+ ext = ".o"
+
+ olist = []
+
+ for f in string.split(dict["sources"]):
+ root, discard = os.path.splitext(f)
+ olist.append(root + ext)
+
+ for f in string.split(dict["moc_headers"]):
+ if not self._qt:
+ error("\"%s\" defines \"moc_headers\" for a non-Qt module." % bfname)
+
+ root, discard = os.path.splitext(f)
+ olist.append("moc_" + root + ext)
+
+ dict["objects"] = string.join(olist)
+
+ return dict
+
+ def clean_build_file_objects(self, mfile, build):
+ """Generate the clean target.
+
+ mfile is the file object.
+ build is the dictionary created from the build file.
+ """
+ mfile.write("\t-%s $(TARGET)\n" % self.rm)
+
+ for f in string.split(build["objects"]):
+ mfile.write("\t-%s %s\n" % (self.rm, f))
+
+ for f in string.split(build["moc_headers"]):
+ root, discard = os.path.splitext(f)
+ mfile.write("\t-%s moc_%s.cpp\n" % (self.rm, root))
+
+ def ready(self):
+ """The Makefile is now ready to be used.
+ """
+ if not self._finalised:
+ self.finalise()
+
+ def generate(self):
+ """Generate the Makefile.
+ """
+ self.ready()
+
+ if self._dir:
+ mfname = os.path.join(self._dir, self._makefile)
+ else:
+ mfname = self._makefile
+
+ try:
+ mfile = open(mfname, "w")
+ except IOError, detail:
+ error("Unable to create \"%s\": %s" % (mfname, detail))
+
+ self.generate_macros_and_rules(mfile)
+ self.generate_target_default(mfile)
+ self.generate_target_install(mfile)
+
+ if self._installs:
+ if type(self._installs) != types.ListType:
+ self._installs = [self._installs]
+
+ for src, dst in self._installs:
+ self.install_file(mfile, src, dst)
+
+ self.generate_target_clean(mfile)
+
+ mfile.close()
+
+ def generate_macros_and_rules(self, mfile):
+ """The default implementation of the macros and rules generation.
+
+ mfile is the file object.
+ """
+ mfile.write("CC = %s\n" % self.required_string("CC"))
+ mfile.write("CXX = %s\n" % self.required_string("CXX"))
+ mfile.write("LINK = %s\n" % self.required_string("LINK"))
+
+ cppflags = []
+
+ for f in self.optional_list("DEFINES"):
+ cppflags.append("-D" + f)
+
+ for f in self.optional_list("INCDIR"):
+ cppflags.append("-I" + _quote(f))
+
+ libs = []
+
+ if self.generator in ("MSVC", "MSVC.NET"):
+ libdir_prefix = "/LIBPATH:"
+ else:
+ libdir_prefix = "-L"
+
+ for ld in self.optional_list("LIBDIR"):
+ if sys.platform == "darwin" and self.config.qt_framework:
+ fflag = "-F" + _quote(ld)
+ libs.append(fflag)
+ cppflags.append(fflag)
+
+ libs.append(libdir_prefix + _quote(ld))
+
+ libs.extend(self.optional_list("LIBS"))
+
+ mfile.write("CPPFLAGS = %s\n" % string.join(cppflags))
+
+ mfile.write("CFLAGS = %s\n" % self.optional_string("CFLAGS"))
+ mfile.write("CXXFLAGS = %s\n" % self.optional_string("CXXFLAGS"))
+ mfile.write("LFLAGS = %s\n" % self.optional_string("LFLAGS"))
+
+ mfile.write("LIBS = %s\n" % string.join(libs))
+
+ if self._qt:
+ mfile.write("MOC = %s\n" % _quote(self.required_string("MOC")))
+
+ # These probably don't matter.
+ if self.generator == "MINGW":
+ mfile.write(".SUFFIXES: .cpp .cxx .cc .C .c\n\n")
+ elif self.generator == "UNIX":
+ mfile.write(".SUFFIXES: .c .o .cpp .cc .cxx .C\n\n")
+ else:
+ mfile.write(".SUFFIXES: .c .cpp .cc .cxx .C\n\n")
+
+ if self.generator in ("MSVC", "MSVC.NET"):
+ mfile.write("""
+{.}.cpp{}.obj::
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -Fo @<<
+\t$<
+<<
+
+{.}.cc{}.obj::
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -Fo @<<
+\t$<
+<<
+
+{.}.cxx{}.obj::
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -Fo @<<
+\t$<
+<<
+
+{.}.C{}.obj::
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -Fo @<<
+\t$<
+<<
+
+{.}.c{}.obj::
+\t$(CC) -c $(CFLAGS) $(CPPFLAGS) -Fo @<<
+\t$<
+<<
+""")
+ elif self.generator == "BMAKE":
+ mfile.write("""
+.cpp.obj:
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o$@ $<
+
+.cc.obj:
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o$@ $<
+
+.cxx.obj:
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o$@ $<
+
+.C.obj:
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o$@ $<
+
+.c.obj:
+\t$(CC) -c $(CFLAGS) $(CPPFLAGS) -o$@ $<
+""")
+ else:
+ mfile.write("""
+.cpp.o:
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o $@ $<
+
+.cc.o:
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o $@ $<
+
+.cxx.o:
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o $@ $<
+
+.C.o:
+\t$(CXX) -c $(CXXFLAGS) $(CPPFLAGS) -o $@ $<
+
+.c.o:
+\t$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
+""")
+
+ def generate_target_default(self, mfile):
+ """The default implementation of the default target.
+
+ mfile is the file object.
+ """
+ mfile.write("\nall:\n")
+
+ def generate_target_install(self, mfile):
+ """The default implementation of the install target.
+
+ mfile is the file object.
+ """
+ mfile.write("\ninstall:\n")
+
+ def generate_target_clean(self, mfile):
+ """The default implementation of the clean target.
+
+ mfile is the file object.
+ """
+ mfile.write("\nclean:\n")
+
+ def install_file(self, mfile, src, dst, strip=0):
+ """Install one or more files in a directory.
+
+ mfile is the file object.
+ src is the name of a single file to install, or the list of a number of
+ files to install.
+ dst is the name of the destination directory.
+ strip is set if the files should be stripped after been installed.
+ """
+ # Help package builders.
+ if self.generator == "UNIX":
+ dst = "$(DESTDIR)" + dst
+
+ mfile.write("\t@%s %s " % (self.chkdir, _quote(dst)))
+
+ if self.generator == "UNIX":
+ mfile.write("|| ")
+
+ mfile.write("%s %s\n" % (self.mkdir, _quote(dst)))
+
+ if type(src) != types.ListType:
+ src = [src]
+
+ # Get the strip command if needed.
+ if strip:
+ strip_cmd = self.optional_string("STRIP")
+
+ if not strip_cmd:
+ strip = 0
+
+ for sf in src:
+ target = _quote(os.path.join(dst, os.path.basename(sf)))
+
+ mfile.write("\t%s %s %s\n" % (self.copy, _quote(sf), target))
+
+ if strip:
+ mfile.write("\t%s %s\n" % (strip_cmd, target))
+
+
+class ParentMakefile(Makefile):
+ """The class that represents a parent Makefile.
+ """
+ def __init__(self, configuration, subdirs, dir=None, makefile="Makefile",
+ installs=None):
+ """Initialise an instance of a parent Makefile.
+
+ subdirs is the sequence of subdirectories.
+ """
+ Makefile.__init__(self, configuration, dir=dir, makefile=makefile, installs=installs)
+
+ self._subdirs = subdirs
+
+ def generate_macros_and_rules(self, mfile):
+ """Generate the macros and rules.
+
+ mfile is the file object.
+ """
+ # We don't want them.
+ pass
+
+ def generate_target_default(self, mfile):
+ """Generate the default target.
+
+ mfile is the file object.
+ """
+ self._subdir_target(mfile)
+
+ def generate_target_install(self, mfile):
+ """Generate the install target.
+
+ mfile is the file object.
+ """
+ self._subdir_target(mfile, "install")
+
+ def generate_target_clean(self, mfile):
+ """Generate the clean target.
+
+ mfile is the file object.
+ """
+ self._subdir_target(mfile, "clean")
+
+ def _subdir_target(self, mfile, target="all"):
+ """Create a target for a list of sub-directories.
+
+ mfile is the file object.
+ target is the name of the target.
+ """
+ if target == "all":
+ tname = ""
+ else:
+ tname = " " + target
+
+ mfile.write("\n" + target + ":\n")
+
+ for d in self._subdirs:
+ if self.generator == "MINGW":
+ mfile.write("\t@$(MAKE) -C %s%s\n" % (d, tname))
+ elif self.generator == "UNIX":
+ mfile.write("\t@(cd %s; $(MAKE)%s)\n" % (d, tname))
+ else:
+ mfile.write("\tcd %s\n" % d)
+ mfile.write("\t$(MAKE)%s\n" % tname)
+ mfile.write("\t@cd ..\n")
+
+
+class PythonModuleMakefile(Makefile):
+ """The class that represents a Python module Makefile.
+ """
+ def __init__(self, configuration, dstdir, srcdir=None, dir=None,
+ makefile="Makefile", installs=None):
+ """Initialise an instance of a parent Makefile.
+
+ dstdir is the name of the directory where the module's Python code will
+ be installed.
+ srcdir is the name of the directory (relative to the directory in which
+ the Makefile will be created) containing the module's Python code. It
+ defaults to the same directory.
+ """
+ Makefile.__init__(self, configuration, dir=dir, makefile=makefile, installs=installs)
+
+ if not srcdir:
+ srcdir = "."
+
+ if dir:
+ self._moddir = os.path.join(dir, srcdir)
+ else:
+ self._moddir = srcdir
+
+ self._srcdir = srcdir
+ self._dstdir = dstdir
+
+ def generate_macros_and_rules(self, mfile):
+ """Generate the macros and rules.
+
+ mfile is the file object.
+ """
+ # We don't want them.
+ pass
+
+ def generate_target_install(self, mfile):
+ """Generate the install target.
+
+ mfile is the file object.
+ """
+ Makefile.generate_target_install(self, mfile)
+
+ os.path.walk(self._moddir, self._visit, mfile)
+
+ def _visit(self, mfile, dirname, names):
+ """Install the files from a particular directory.
+
+ mfile is the file object.
+ dirname is the sub-directory.
+ names is the list of files to install from the sub-directory.
+ """
+ tail = dirname[len(self._moddir):]
+
+ flist = []
+ for f in names:
+ # Ignore certain files.
+ if f in ("Makefile", ):
+ continue
+
+ if os.path.isfile(os.path.join(dirname, f)):
+ flist.append(os.path.join(self._srcdir + tail, f))
+
+ self.install_file(mfile, flist, self._dstdir + tail)
+
+
+class ModuleMakefile(Makefile):
+ """The class that represents a Python extension module Makefile
+ """
+ def __init__(self, configuration, build_file, install_dir=None, static=0,
+ console=0, qt=0, opengl=0, threaded=0, warnings=1, debug=0,
+ dir=None, makefile="Makefile", installs=None, strip=1,
+ export_all=0, universal=''):
+ """Initialise an instance of a module Makefile.
+
+ build_file is the file containing the target specific information. If
+ it is a dictionary instead then its contents are validated.
+ install_dir is the directory the target will be installed in.
+ static is set if the module should be built as a static library.
+ strip is set if the module should be stripped of unneeded symbols when
+ installed. The default is 1.
+ export_all is set if all the module's symbols should be exported rather
+ than just the module's initialisation function. Exporting all symbols
+ increases the size of the module and slows down module load times but
+ may avoid problems with modules that use exceptions. The default is 0.
+ """
+ Makefile.__init__(self, configuration, console, qt, opengl, 1, threaded, warnings, debug, dir, makefile, installs, universal)
+
+ self._build = self.parse_build_file(build_file)
+ self._install_dir = install_dir
+ self._dir = dir
+ self.static = static
+
+ # Don't strip or restrict the exports if this is a debug or static
+ # build.
+ if debug or static:
+ self._strip = 0
+ self._limit_exports = 0
+ else:
+ self._strip = strip
+
+ # The deprecated configuration flag has precedence.
+ if self.config.export_all:
+ self._limit_exports = 0
+ else:
+ self._limit_exports = not export_all
+
+ # Save the target name for later.
+ self._target = self._build["target"]
+
+ if sys.platform != "win32" and static:
+ self._target = "lib" + self._target
+
+ if sys.platform == "win32" and debug:
+ self._target = self._target + "_d"
+
+ def finalise(self):
+ """Finalise the macros common to all module Makefiles.
+ """
+ if self.console:
+ lflags_console = "LFLAGS_CONSOLE"
+ else:
+ lflags_console = "LFLAGS_WINDOWS"
+
+ if self.static:
+ self.DEFINES.append("SIP_STATIC_MODULE")
+ else:
+ self.CFLAGS.extend(self.optional_list("CFLAGS_SHLIB"))
+ self.CXXFLAGS.extend(self.optional_list("CXXFLAGS_SHLIB"))
+
+ lflags_dll = self.optional_list("LFLAGS_DLL")
+
+ if lflags_dll:
+ self.LFLAGS.extend(lflags_dll)
+ elif self.console:
+ lflags_console = "LFLAGS_CONSOLE_DLL"
+ else:
+ lflags_console = "LFLAGS_WINDOWS_DLL"
+
+ # We use this to explictly create bundles on MacOS. Apple's Python
+ # can handle extension modules that are bundles or dynamic
+ # libraries, but python.org versions need bundles (unless built
+ # with DYNLOADFILE=dynload_shlib.o).
+ if sys.platform == "darwin":
+ lflags_plugin = ["-bundle"]
+ else:
+ lflags_plugin = self.optional_list("LFLAGS_PLUGIN")
+
+ if not lflags_plugin:
+ lflags_plugin = self.optional_list("LFLAGS_SHLIB")
+
+ self.LFLAGS.extend(lflags_plugin)
+
+ self.LFLAGS.extend(self.optional_list(lflags_console))
+
+ if sys.platform == "darwin":
+ # We use the -F flag to explictly specify the directory containing
+ # the Python framework rather than rely on the default search path.
+ # This allows Apple's Python to be used even if a later python.org
+ # version is also installed.
+ dl = string.split(sys.exec_prefix, os.sep)
+ try:
+ dl = dl[:dl.index("Python.framework")]
+ except ValueError:
+ error("SIP requires Python to be built as a framework")
+ self.LFLAGS.append("-F%s" % string.join(dl, os.sep))
+ self.LFLAGS.append("-framework Python")
+
+ Makefile.finalise(self)
+
+ if not self.static:
+ if self.optional_string("AIX_SHLIB"):
+ # AIX needs a lot of special handling.
+ if self.required_string('LINK') == 'g++':
+ # g++ is used for linking.
+ # For SIP v4 and g++:
+ # 1.) Import the python symbols
+ aix_lflags = ['-Wl,-bI:%s/python.exp' % self.config.py_lib_dir]
+
+ if self._limit_exports:
+ aix_lflags.append('-Wl,-bnoexpall')
+ aix_lflags.append('-Wl,-bnoentry')
+ aix_lflags.append('-Wl,-bE:%s.exp' % self._target)
+ else:
+ # IBM VisualAge C++ is used for linking.
+ # For SIP v4 and xlC:
+ # 1.) Create a shared object
+ # 2.) Import the python symbols
+ aix_lflags = ['-qmkshrobj',
+ '-bI:%s/python.exp' % self.config.py_lib_dir]
+
+ if self._limit_exports:
+ aix_lflags.append('-bnoexpall')
+ aix_lflags.append('-bnoentry')
+ aix_lflags.append('-bE:%s.exp' % self._target)
+
+ self.LFLAGS.extend(aix_lflags)
+ else:
+ if self._limit_exports:
+ if sys.platform[:5] == 'linux':
+ self.LFLAGS.extend(['-Wl,--version-script=%s.exp' % self._target])
+ elif sys.platform[:5] == 'sunos':
+ if self.required_string('LINK') == 'g++':
+ self.LFLAGS.extend(['-Wl,-z,noversion', '-Wl,-M,%s.exp' % self._target])
+ else:
+ self.LFLAGS.extend(['-z' 'noversion', '-M', '%s.exp' % self._target])
+ elif sys.platform[:5] == 'hp-ux':
+ self.LFLAGS.extend(['-Wl,+e,init%s' % self._target])
+ elif sys.platform[:5] == 'irix' and self.required_string('LINK') != 'g++':
+ # Doesn't work when g++ is used for linking on IRIX.
+ self.LFLAGS.extend(['-Wl,-exported_symbol,init%s' % self._target])
+
+ # Force the shared linker if there is one.
+ link_shlib = self.optional_list("LINK_SHLIB")
+
+ if link_shlib:
+ self.LINK.set(link_shlib)
+
+ def module_as_lib(self, mname):
+ """Return the name of a SIP v3.x module when it is used as a library.
+ This will raise an exception when used with SIP v4.x modules.
+
+ mname is the name of the module.
+ """
+ raise ValueError, "module_as_lib() can only be used with SIP v3.x"
+
+ def generate_macros_and_rules(self, mfile):
+ """Generate the macros and rules generation.
+
+ mfile is the file object.
+ """
+ if self.static:
+ if sys.platform == "win32":
+ ext = "lib"
+ else:
+ ext = "a"
+ else:
+ if sys.platform == "win32":
+ ext = "pyd"
+ elif sys.platform == "darwin":
+ ext = "so"
+ elif sys.platform == "cygwin":
+ ext = "dll"
+ else:
+ ext = self.optional_string("EXTENSION_PLUGIN")
+ if not ext:
+ ext = self.optional_string("EXTENSION_SHLIB", "so")
+
+ mfile.write("TARGET = %s\n" % (self._target + "." + ext))
+ mfile.write("OFILES = %s\n" % self._build["objects"])
+ mfile.write("HFILES = %s %s\n" % (self._build["headers"], self._build["moc_headers"]))
+ mfile.write("\n")
+
+ if self.static:
+ if self.generator in ("MSVC", "MSVC.NET", "BMAKE"):
+ mfile.write("LIB = %s\n" % self.required_string("LIB"))
+ elif self.generator == "MINGW":
+ mfile.write("AR = %s\n" % self.required_string("LIB"))
+ self._ranlib = None
+ else:
+ mfile.write("AR = %s\n" % self.required_string("AR"))
+
+ self._ranlib = self.optional_string("RANLIB")
+
+ if self._ranlib:
+ mfile.write("RANLIB = %s\n" % self._ranlib)
+
+ Makefile.generate_macros_and_rules(self, mfile)
+
+ def generate_target_default(self, mfile):
+ """Generate the default target.
+
+ mfile is the file object.
+ """
+ mfile.write("\n$(TARGET): $(OFILES)\n")
+
+ if self.generator in ("MSVC", "MSVC.NET"):
+ if self.static:
+ mfile.write("\t$(LIB) /OUT:$(TARGET) @<<\n")
+ mfile.write("\t $(OFILES)\n")
+ mfile.write("<<\n")
+ else:
+ mfile.write("\t$(LINK) $(LFLAGS) /OUT:$(TARGET) @<<\n")
+ mfile.write("\t $(OFILES) $(LIBS)\n")
+ mfile.write("<<\n")
+
+ if "embed_manifest_dll" in self.optional_list("CONFIG"):
+ mfile.write("\tmt -nologo -manifest $(TARGET).manifest -outputresource:$(TARGET);2\n")
+ elif self.generator == "BMAKE":
+ if self.static:
+ mfile.write("\t-%s $(TARGET)\n" % (self.rm))
+ mfile.write("\t$(LIB) $(TARGET) @&&|\n")
+
+ for of in string.split(self._build["objects"]):
+ mfile.write("+%s \\\n" % (of))
+
+ mfile.write("|\n")
+ else:
+ mfile.write("\t$(LINK) @&&|\n")
+ mfile.write("\t$(LFLAGS) $(OFILES) ,$(TARGET),,$(LIBS),%s\n" % (self._target))
+ mfile.write("|\n")
+
+ # Create the .def file that renames the entry point.
+ defname = self._target + ".def"
+
+ if self._dir:
+ defname = os.path.join(self._dir, defname)
+
+ try:
+ dfile = open(defname, "w")
+ except IOError, detail:
+ error("Unable to create \"%s\": %s" % (defname, detail))
+
+ dfile.write("EXPORTS\n")
+ dfile.write("init%s=_init%s\n" % (self._target, self._target))
+
+ dfile.close()
+
+ else:
+ if self.static:
+ mfile.write("\t-%s $(TARGET)\n" % self.rm)
+ mfile.write("\t$(AR) $(TARGET) $(OFILES)\n")
+
+ if self._ranlib:
+ mfile.write("\t$(RANLIB) $(TARGET)\n")
+ else:
+ if self._limit_exports:
+ # Create an export file for AIX, Linux and Solaris.
+ if sys.platform[:5] == 'linux':
+ mfile.write("\t@echo '{ global: init%s; local: *; };' > %s.exp\n" % (self._target, self._target))
+ elif sys.platform[:5] == 'sunos':
+ mfile.write("\t@echo '{ global: init%s; local: *; };' > %s.exp\n" % (self._target, self._target))
+ elif sys.platform[:3] == 'aix':
+ mfile.write("\t@echo '#!' >%s.exp" % self._target)
+ mfile.write("; \\\n\t echo 'init%s' >>%s.exp\n" % (self._target, self._target))
+
+ mfile.write("\t$(LINK) $(LFLAGS) -o $(TARGET) $(OFILES) $(LIBS)\n")
+
+ mfile.write("\n$(OFILES): $(HFILES)\n")
+
+ for mf in string.split(self._build["moc_headers"]):
+ root, discard = os.path.splitext(mf)
+ cpp = "moc_" + root + ".cpp"
+
+ mfile.write("\n%s: %s\n" % (cpp, mf))
+ mfile.write("\t$(MOC) -o %s %s\n" % (cpp, mf))
+
+ def generate_target_install(self, mfile):
+ """Generate the install target.
+
+ mfile is the file object.
+ """
+ if self._install_dir is None:
+ self._install_dir = self.config.default_mod_dir
+
+ mfile.write("\ninstall: $(TARGET)\n")
+ self.install_file(mfile, "$(TARGET)", self._install_dir, self._strip)
+
+ def generate_target_clean(self, mfile):
+ """Generate the clean target.
+
+ mfile is the file object.
+ """
+ mfile.write("\nclean:\n")
+ self.clean_build_file_objects(mfile, self._build)
+
+ # Remove any export file on AIX, Linux and Solaris.
+ if self._limit_exports and (sys.platform[:5] == 'linux' or
+ sys.platform[:5] == 'sunos' or
+ sys.platform[:3] == 'aix'):
+ mfile.write("\t-%s %s.exp\n" % (self.rm, self._target))
+
+
+class SIPModuleMakefile(ModuleMakefile):
+ """The class that represents a SIP generated module Makefile.
+ """
+ def finalise(self):
+ """Finalise the macros for a SIP generated module Makefile.
+ """
+ self.INCDIR.append(self.config.sip_inc_dir)
+
+ ModuleMakefile.finalise(self)
+
+
+class ProgramMakefile(Makefile):
+ """The class that represents a program Makefile.
+ """
+ def __init__(self, configuration, build_file=None, install_dir=None,
+ console=0, qt=0, opengl=0, python=0, threaded=0, warnings=1,
+ debug=0, dir=None, makefile="Makefile", installs=None,
+ universal=''):
+ """Initialise an instance of a program Makefile.
+
+ build_file is the file containing the target specific information. If
+ it is a dictionary instead then its contents are validated.
+ install_dir is the directory the target will be installed in.
+ """
+ Makefile.__init__(self, configuration, console, qt, opengl, python, threaded, warnings, debug, dir, makefile, installs, universal)
+
+ self._install_dir = install_dir
+
+ if build_file:
+ self._build = self.parse_build_file(build_file)
+ else:
+ self._build = None
+
+ def build_command(self, source):
+ """Create a command line that will build an executable. Returns a
+ tuple of the name of the executable and the command line.
+
+ source is the name of the source file.
+ """
+ self.ready()
+
+ # The name of the executable.
+ exe, ignore = os.path.splitext(source)
+
+ if sys.platform in ("win32", "cygwin"):
+ exe = exe + ".exe"
+
+ # The command line.
+ build = []
+
+ build.append(self.required_string("CXX"))
+
+ for f in self.optional_list("DEFINES"):
+ build.append("-D" + f)
+
+ for f in self.optional_list("INCDIR"):
+ build.append("-I" + _quote(f))
+
+ build.extend(self.optional_list("CXXFLAGS"))
+
+ # Borland requires all flags to precede all file names.
+ if self.generator != "BMAKE":
+ build.append(source)
+
+ if self.generator in ("MSVC", "MSVC.NET"):
+ build.append("-Fe")
+ build.append("/link")
+ libdir_prefix = "/LIBPATH:"
+ elif self.generator == "BMAKE":
+ build.append("-e" + exe)
+ libdir_prefix = "-L"
+ else:
+ build.append("-o")
+ build.append(exe)
+ libdir_prefix = "-L"
+
+ for ld in self.optional_list("LIBDIR"):
+ if sys.platform == "darwin" and self.config.qt_framework:
+ build.append("-F" + _quote(ld))
+
+ build.append(libdir_prefix + _quote(ld))
+
+ lflags = self.optional_list("LFLAGS")
+
+ # This is a huge hack demonstrating my lack of understanding of how the
+ # Borland compiler works.
+ if self.generator == "BMAKE":
+ blflags = []
+
+ for lf in lflags:
+ for f in string.split(lf):
+ # Tell the compiler to pass the flags to the linker.
+ if f[-1] == "-":
+ f = "-l-" + f[1:-1]
+ elif f[0] == "-":
+ f = "-l" + f[1:]
+
+ # Remove any explicit object files otherwise the compiler
+ # will complain that they can't be found, but they don't
+ # seem to be needed.
+ if string.lower(f[-4:]) != ".obj":
+ blflags.append(f)
+
+ lflags = blflags
+
+ build.extend(lflags)
+
+ build.extend(self.optional_list("LIBS"))
+
+ if self.generator == "BMAKE":
+ build.append(source)
+
+ return (exe, string.join(build))
+
+ def finalise(self):
+ """Finalise the macros for a program Makefile.
+ """
+ if self.generator in ("MSVC", "MSVC.NET"):
+ self.LFLAGS.append("/INCREMENTAL:NO")
+
+ if self.console:
+ lflags_console = "LFLAGS_CONSOLE"
+ else:
+ lflags_console = "LFLAGS_WINDOWS"
+
+ self.LFLAGS.extend(self.optional_list(lflags_console))
+
+ Makefile.finalise(self)
+
+ def generate_macros_and_rules(self, mfile):
+ """Generate the macros and rules generation.
+
+ mfile is the file object.
+ """
+ if not self._build:
+ raise ValueError, "pass a filename as build_file when generating a Makefile"
+
+ target = self._build["target"]
+
+ if sys.platform in ("win32", "cygwin"):
+ target = target + ".exe"
+
+ mfile.write("TARGET = %s\n" % target)
+ mfile.write("OFILES = %s\n" % self._build["objects"])
+ mfile.write("HFILES = %s\n" % self._build["headers"])
+ mfile.write("\n")
+
+ Makefile.generate_macros_and_rules(self, mfile)
+
+ def generate_target_default(self, mfile):
+ """Generate the default target.
+
+ mfile is the file object.
+ """
+ mfile.write("\n$(TARGET): $(OFILES)\n")
+
+ if self.generator in ("MSVC", "MSVC.NET"):
+ mfile.write("\t$(LINK) $(LFLAGS) /OUT:$(TARGET) @<<\n")
+ mfile.write("\t $(OFILES) $(LIBS)\n")
+ mfile.write("<<\n")
+
+ if "embed_manifest_dll" in self.optional_list("CONFIG"):
+ mfile.write("\tmt -nologo -manifest $(TARGET).manifest -outputresource:$(TARGET);1\n")
+ elif self.generator == "BMAKE":
+ mfile.write("\t$(LINK) @&&|\n")
+ mfile.write("\t$(LFLAGS) $(OFILES) ,$(TARGET),,$(LIBS),,\n")
+ mfile.write("|\n")
+ else:
+ mfile.write("\t$(LINK) $(LFLAGS) -o $(TARGET) $(OFILES) $(LIBS)\n")
+
+ mfile.write("\n$(OFILES): $(HFILES)\n")
+
+ for mf in string.split(self._build["moc_headers"]):
+ root, discard = os.path.splitext(mf)
+ cpp = "moc_" + root + ".cpp"
+
+ mfile.write("\n%s: %s\n" % (cpp, mf))
+ mfile.write("\t$(MOC) -o %s %s\n" % (cpp, mf))
+
+ def generate_target_install(self, mfile):
+ """Generate the install target.
+
+ mfile is the file object.
+ """
+ if self._install_dir is None:
+ self._install_dir = self.config.default_bin_dir
+
+ mfile.write("\ninstall: $(TARGET)\n")
+ self.install_file(mfile, "$(TARGET)", self._install_dir)
+
+ def generate_target_clean(self, mfile):
+ """Generate the clean target.
+
+ mfile is the file object.
+ """
+ mfile.write("\nclean:\n")
+ self.clean_build_file_objects(mfile, self._build)
+
+
+def _quote(s):
+ """Return a string surrounded by double quotes it if contains a space.
+
+ s is the string.
+ """
+ if string.find(s, " ") >= 0:
+ s = '"' + s + '"'
+
+ return s
+
+
+def version_to_string(v):
+ """Convert a 3 part version number encoded as a hexadecimal value to a
+ string.
+ """
+ return "%u.%u.%u" % (((v >> 16) & 0xff), ((v >> 8) & 0xff), (v & 0xff))
+
+
+def read_version(filename, description, numdefine=None, strdefine=None):
+ """Read the version information for a package from a file. The information
+ is specified as #defines of a numeric (hexadecimal or decimal) value and/or
+ a string value.
+
+ filename is the name of the file.
+ description is the descriptive name of the package.
+ numdefine is the name of the #define of the numeric version. It is ignored
+ if it is None.
+ strdefine is the name of the #define of the string version. It is ignored
+ if it is None.
+
+ Returns a tuple of the version as a number and as a string.
+ """
+ need_num = numdefine is not None
+ need_str = strdefine is not None
+
+ vers = None
+ versstr = None
+
+ f = open(filename)
+ l = f.readline()
+
+ while l and (need_num or need_str):
+ wl = string.split(l)
+ if len(wl) >= 3 and wl[0] == "#define":
+ if need_num and wl[1] == numdefine:
+ v = wl[2]
+
+ if v[0:2] == "0x":
+ vers = string.atoi(v,16)
+ else:
+ dec = int(v)
+ maj = dec / 100
+ min = (dec % 100) / 10
+ bug = (dec % 10)
+ vers = (maj << 16) + (min << 8) + bug
+
+ need_num = 0
+
+ if need_str and wl[1] == strdefine:
+ # Take account of embedded spaces.
+ versstr = string.join(wl[2:])[1:-1]
+ need_str = 0
+
+ l = f.readline()
+
+ f.close()
+
+ if need_num or need_str:
+ error("The %s version number could not be determined by parsing %s." % (description, filename))
+
+ return (vers, versstr)
+
+
+def create_content(dict, macros=None):
+ """Convert a dictionary to a string (typically to use as the content to a
+ call to create_config_module()). Dictionary values that are strings are
+ quoted. Dictionary values that are lists are converted to quoted strings.
+
+ dict is the dictionary.
+ macros is the optional dictionary of platform specific build macros.
+ """
+ content = "_pkg_config = {\n"
+
+ keys = dict.keys()
+ keys.sort()
+
+ # Format it nicely.
+ width = 0
+
+ for k in keys:
+ klen = len(k)
+
+ if width < klen:
+ width = klen
+
+ for k in keys:
+ val = dict[k]
+ vtype = type(val)
+
+ if val is None:
+ val = "None"
+ elif vtype == types.ListType:
+ val = "'" + string.join(val) + "'"
+ elif vtype == types.StringType:
+ val = "'" + val + "'"
+ elif vtype == types.IntType:
+ if string.find(k, "version") >= 0:
+ # Assume it's a hexadecimal version number. It doesn't matter
+ # if it isn't, we are just trying to make it look pretty.
+ val = "0x%06x" % val
+ else:
+ val = str(val)
+ else:
+ val = "'" + str(val) + "'"
+
+ content = content + " '" + k + "':" + (" " * (width - len(k) + 2)) + string.replace(val, "\\", "\\\\")
+
+ if k != keys[-1]:
+ content = content + ","
+
+ content = content + "\n"
+
+ content = content + "}\n\n"
+
+ # Format the optional macros.
+ content = content + "_default_macros = "
+
+ if macros:
+ content = content + "{\n"
+
+ names = macros.keys()
+ names.sort()
+
+ width = 0
+ for c in names:
+ clen = len(c)
+ if width < clen:
+ width = clen
+
+ for c in names:
+ if c == names[-1]:
+ sep = ""
+ else:
+ sep = ","
+
+ k = "'" + c + "':"
+ content = content + " %-*s '%s'%s\n" % (1 + width + 2, k, string.replace(macros[c], "\\", "\\\\"), sep)
+
+ content = content + "}\n"
+ else:
+ content = content + "None\n"
+
+ return content
+
+
+def create_config_module(module, template, content, macros=None):
+ """Create a configuration module by replacing "@" followed by
+ "SIP_CONFIGURATION" followed by "@" in a template file with a content
+ string.
+
+ module is the name of the module file.
+ template is the name of the template file.
+ content is the content string. If it is a dictionary it is first converted
+ to a string using create_content().
+ macros is an optional dictionary of platform specific build macros. It is
+ only used if create_content() is called to convert the content to a string.
+ """
+ if type(content) == types.DictType:
+ content = create_content(content, macros)
+
+ # Allow this file to used as a template.
+ key = "@" + "SIP_CONFIGURATION" + "@"
+
+ df = open(module, "w")
+ sf = open(template, "r")
+
+ line = sf.readline()
+ while line:
+ if string.find(line, key) >= 0:
+ line = content
+
+ df.write(line)
+
+ line = sf.readline()
+
+
+def version_to_sip_tag(version, tags, description):
+ """Convert a version number to a SIP tag.
+
+ version is the version number. If it is negative then the latest version
+ is assumed. (This is typically useful if a snapshot is indicated by a
+ negative version number.)
+ tags is the dictionary of tags keyed by version number. The tag used is
+ the one with the smallest key (ie. earliest version) that is greater than
+ the given version number.
+ description is the descriptive name of the package used for error messages.
+
+ Returns the corresponding tag.
+ """
+ vl = tags.keys()
+ vl.sort()
+
+ # For a snapshot use the latest tag.
+ if version < 0:
+ tag = tags[vl[-1]]
+ else:
+ for v in vl:
+ if version < v:
+ tag = tags[v]
+ break
+ else:
+ error("Unsupported %s version: 0x%06x." % (description, version))
+
+ return tag
+
+
+def error(msg):
+ """Display an error message and terminate.
+
+ msg is the text of the error message.
+ """
+ sys.stderr.write(format("Error: " + msg) + "\n")
+ sys.exit(1)
+
+
+def inform(msg):
+ """Display an information message.
+
+ msg is the text of the error message.
+ """
+ sys.stdout.write(format(msg) + "\n")
+
+
+def format(msg, leftmargin=0, rightmargin=78):
+ """Format a message by inserting line breaks at appropriate places.
+
+ msg is the text of the message.
+ leftmargin is the position of the left margin.
+ rightmargin is the position of the right margin.
+
+ Return the formatted message.
+ """
+ curs = leftmargin
+ fmsg = " " * leftmargin
+
+ for w in string.split(msg):
+ l = len(w)
+ if curs != leftmargin and curs + l > rightmargin:
+ fmsg = fmsg + "\n" + (" " * leftmargin)
+ curs = leftmargin
+
+ if curs > leftmargin:
+ fmsg = fmsg + " "
+ curs = curs + 1
+
+ fmsg = fmsg + w
+ curs = curs + l
+
+ return fmsg
+
+
+def parse_build_macros(filename, names, overrides=None, properties=None):
+ """Parse a qmake compatible file of build system macros and convert it to a
+ dictionary. A macro is a name/value pair. The dictionary is returned or
+ None if any of the overrides was invalid.
+
+ filename is the name of the file to parse.
+ names is a list of the macro names to extract from the file.
+ overrides is an optional list of macro names and values that modify those
+ found in the file. They are of the form "name=value" (in which case the
+ value replaces the value found in the file) or "name+=value" (in which case
+ the value is appended to the value found in the file).
+ properties is an optional dictionary of property name and values that are
+ used to resolve any expressions of the form "$[name]" in the file.
+ """
+ # Validate and convert the overrides to a dictionary.
+ orides = {}
+
+ if overrides is not None:
+ for oride in overrides:
+ prefix = ""
+ name_end = string.find(oride, "+=")
+
+ if name_end >= 0:
+ prefix = "+"
+ val_start = name_end + 2
+ else:
+ name_end = string.find(oride, "=")
+
+ if name_end >= 0:
+ val_start = name_end + 1
+ else:
+ return None
+
+ name = oride[:name_end]
+
+ if name not in names:
+ return None
+
+ orides[name] = prefix + oride[val_start:]
+
+ # This class defines a file like object that handles the nested include()
+ # directives in qmake files.
+ class qmake_build_file_reader:
+ def __init__(self, filename):
+ self.filename = filename
+ self.currentfile = None
+ self.filestack = []
+ self.pathstack = []
+ self.cond_fname = None
+ self._openfile(filename)
+
+ def _openfile(self, filename):
+ try:
+ f = open(filename, 'r')
+ except IOError, detail:
+ # If this file is conditional then don't raise an error.
+ if self.cond_fname == filename:
+ return
+
+ error("Unable to open %s: %s" % (filename, detail))
+
+ if self.currentfile:
+ self.filestack.append(self.currentfile)
+ self.pathstack.append(self.path)
+
+ self.currentfile = f
+ self.path = os.path.dirname(filename)
+
+ def readline(self):
+ line = self.currentfile.readline()
+ sline = line.strip()
+
+ if self.cond_fname and sline == '}':
+ # The current condition is closed.
+ self.cond_fname = None
+ line = self.currentfile.readline()
+ elif sline.startswith('exists(') and sline.endswith('{'):
+ # A new condition is opened so extract the filename.
+ self.cond_fname = self._normalise(sline[:-1].strip()[7:-1].strip())
+ line = self.currentfile.readline()
+ elif sline.startswith('include('):
+ nextfile = self._normalise(sline[8:-1].strip())
+ self._openfile(nextfile)
+ return self.readline()
+
+ if not line and self.filestack:
+ self.currentfile = self.filestack.pop()
+ self.path = self.pathstack.pop()
+ return self.readline()
+
+ return line
+
+ # Normalise a filename by expanding any environment variables and
+ # making sure it is absolute.
+ def _normalise(self, fname):
+ if "$(" in fname:
+ fname = os.path.normpath(self._expandvars(fname))
+
+ if not os.path.isabs(fname):
+ fname = os.path.join(self.path, fname)
+
+ return fname
+
+ # Expand the environment variables in a filename.
+ def _expandvars(self, fname):
+ i = 0
+ while True:
+ m = re.search(r'\$\((\w+)\)', fname[i:])
+ if not m:
+ break
+
+ i, j = m.span(0)
+ name = m.group(1)
+ if name in os.environ:
+ tail = fname[j:]
+ fname = fname[:i] + os.environ[name]
+ i = len(fname)
+ fname += tail
+ else:
+ i = j
+
+ return fname
+
+ f = qmake_build_file_reader(filename)
+
+ # Get everything into a dictionary.
+ raw = {
+ "DIR_SEPARATOR": os.sep,
+ "LITERAL_WHITESPACE": " ",
+ "LITERAL_DOLLAR": "$",
+ "LITERAL_HASH": "#"
+ }
+
+ line = f.readline()
+ while line:
+ # Handle line continuations.
+ while len(line) > 1 and line[-2] == "\\":
+ line = line[:-2]
+
+ next = f.readline()
+
+ if next:
+ line = line + next
+ else:
+ break
+
+ line = string.strip(line)
+
+ # Ignore comments.
+ if line and line[0] != "#":
+ assstart = string.find(line, "+")
+ if assstart > 0 and line[assstart + 1] == '=':
+ assend = assstart + 1
+ else:
+ assstart = string.find(line, "=")
+ assend = assstart
+
+ if assstart > 0:
+ lhs = string.strip(line[:assstart])
+ rhs = string.strip(line[assend + 1:])
+
+ raw[lhs] = rhs
+
+ line = f.readline()
+
+ # Go through the raw dictionary extracting the macros we need and
+ # resolving any macro expansions. First of all, make sure every macro has
+ # a value.
+ refined = {}
+
+ for m in names:
+ refined[m] = ""
+
+ macro_prefix = "QMAKE_"
+
+ for lhs in raw.keys():
+ # Strip any prefix.
+ if string.find(lhs, macro_prefix) == 0:
+ reflhs = lhs[len(macro_prefix):]
+ else:
+ reflhs = lhs
+
+ # See if we are interested in this one.
+ if reflhs not in names:
+ continue
+
+ rhs = raw[lhs]
+
+ # Resolve any references.
+ estart = string.find(rhs, "$$(")
+ mstart = string.find(rhs, "$$")
+
+ while mstart >= 0 and mstart != estart:
+ rstart = mstart + 2
+ if rstart < len(rhs) and rhs[rstart] == "{":
+ rstart = rstart + 1
+ term = "}"
+ elif rstart < len(rhs) and rhs[rstart] == "[":
+ rstart = rstart + 1
+ term = "]"
+ else:
+ term = string.whitespace
+
+ mend = rstart
+ while mend < len(rhs) and rhs[mend] not in term:
+ mend = mend + 1
+
+ lhs = rhs[rstart:mend]
+
+ if term in "}]":
+ mend = mend + 1
+
+ if term == "]":
+ if properties is None or lhs not in properties.keys():
+ error("%s: property '%s' is not defined." % (filename, lhs))
+
+ value = properties[lhs]
+ else:
+ try:
+ value = raw[lhs]
+ except KeyError:
+ error("%s: macro '%s' is not defined." % (filename, lhs))
+
+ rhs = rhs[:mstart] + value + rhs[mend:]
+ estart = string.find(rhs, "$$(")
+ mstart = string.find(rhs, "$$")
+
+ # Expand any POSIX style environment variables.
+ pleadin = ["$$(", "$("]
+
+ for pl in pleadin:
+ estart = string.find(rhs, pl)
+
+ if estart >= 0:
+ nstart = estart + len(pl)
+ break
+ else:
+ estart = -1
+
+ while estart >= 0:
+ eend = string.find(rhs[nstart:], ")")
+
+ if eend < 0:
+ break
+
+ eend = nstart + eend
+
+ name = rhs[nstart:eend]
+
+ try:
+ env = os.environ[name]
+ except KeyError:
+ env = ""
+
+ rhs = rhs[:estart] + env + rhs[eend + 1:]
+
+ for pl in pleadin:
+ estart = string.find(rhs, pl)
+
+ if estart >= 0:
+ nstart = estart + len(pl)
+ break
+ else:
+ estart = -1
+
+ # Expand any Windows style environment variables.
+ estart = string.find(rhs, "%")
+
+ while estart >= 0:
+ eend = string.find(rhs[estart + 1:], "%")
+
+ if eend < 0:
+ break
+
+ eend = estart + 1 + eend
+
+ name = rhs[estart + 1:eend]
+
+ try:
+ env = os.environ[name]
+ except KeyError:
+ env = ""
+
+ rhs = rhs[:estart] + env + rhs[eend + 1:]
+
+ estart = string.find(rhs, "%")
+
+ refined[reflhs] = rhs
+
+ # Handle the user overrides.
+ for lhs in orides.keys():
+ rhs = refined[lhs]
+ oride = orides[lhs]
+
+ if string.find(oride, "+") == 0:
+ if rhs:
+ rhs = rhs + " " + oride[1:]
+ else:
+ rhs = oride[1:]
+ else:
+ rhs = oride
+
+ refined[lhs] = rhs
+
+ return refined
+
+
+def create_wrapper(script, wrapper, gui=0):
+ """Create a platform dependent executable wrapper around a Python script.
+
+ script is the full pathname of the script.
+ wrapper is the name of the wrapper file to create.
+ gui is non-zero if a GUI enabled version of the interpreter should be used.
+
+ Returns the platform specific name of the wrapper.
+ """
+ if sys.platform == "win32":
+ wrapper = wrapper + ".bat"
+
+ wf = open(wrapper, "w")
+
+ if sys.platform == "win32":
+ exe = sys.executable
+
+ if gui:
+ exe = exe[:-4] + "w.exe"
+
+ wf.write("@\"%s\" \"%s\" %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9\n" % (exe, script))
+ elif sys.platform == "darwin":
+ # python, pythonw and sys.executable are all different images. We
+ # would prefer to use the latter (because it includes the version
+ # number) but that would mean being unable to support the "gui"
+ # argument.
+ if gui:
+ exe = "pythonw"
+ else:
+ exe = "python"
+
+ wf.write("exec %s %s ${1+\"$@\"}\n" % (exe, script))
+ else:
+ wf.write("exec %s %s ${1+\"$@\"}\n" % (sys.executable, script))
+
+ wf.close()
+
+ if sys.platform != "win32":
+ sbuf = os.stat(wrapper)
+ mode = sbuf.st_mode
+ mode |= (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
+
+ os.chmod(wrapper, mode)
+
+ return wrapper