summaryrefslogtreecommitdiffstats
path: root/siputils.py
diff options
context:
space:
mode:
Diffstat (limited to 'siputils.py')
-rw-r--r--siputils.py2525
1 files changed, 2525 insertions, 0 deletions
diff --git a/siputils.py b/siputils.py
new file mode 100644
index 0000000..bb5959e
--- /dev/null
+++ b/siputils.py
@@ -0,0 +1,2525 @@
+# 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) 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.
+
+
+import sys
+import os
+import stat
+import string
+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) == list:
+ 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 remove(self, value):
+ """Remove a value from the macro. It doesn't matter if the value
+ wasn't present.
+
+ value is the value to remove.
+ """
+ try:
+ self._macro.remove(value)
+ except:
+ pass
+
+ 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=None,
+ arch=None):
+ """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 it is None then the value is taken from the configuration.
+ arch is the space separated MacOS/X architectures to build. If it is
+ None then it is taken from the configuration.
+ """
+ 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) != list:
+ qt = ["QtCore", "QtGui"]
+
+ self._threaded = configuration.qt_threaded
+ else:
+ self._threaded = threaded
+
+ self.config = configuration
+ self.console = console
+ self._qt = qt
+ self._opengl = opengl
+ self._python = python
+ self._warnings = warnings
+ self._debug = debug
+ self._makefile = makefile
+ self._installs = installs
+
+ # Make sure the destination directory is an absolute path.
+ if dir:
+ self.dir = os.path.abspath(dir)
+ else:
+ self.dir = os.path.curdir
+
+ # Assume we are building in the source tree.
+ self._src_dir = self.dir
+
+ if universal is None:
+ self._universal = configuration.universal
+ else:
+ self._universal = universal
+
+ if arch is None:
+ self._arch = configuration.arch
+ else:
+ self._arch = arch
+
+ self._finalised = 0
+
+ # Copy the macros and convert them all to instance lists.
+ macros = configuration.build_macros()
+
+ for m in list(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 = val.split()
+
+ # We also want to treat lists of libraries in the same way so that
+ # duplicates get eliminated.
+ if m[:4] == "LIBS":
+ val = val.split()
+
+ 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.
+ # Note that we used to think that if Qt was built with exceptions, RTTI
+ # and STL support enabled then anything that linked against it also
+ # needed the same flags. However, detecting this was broken for some
+ # time and nobody complained. For the moment we'll leave the code in
+ # but it will never be used.
+ if self._qt:
+ wcfg = self.config.qt_winconfig.split()
+ 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 specific configuration.
+ if sys.platform == 'darwin':
+ mac_cflags = []
+ mac_lflags = []
+
+ for a in self._arch.split():
+ aflag = '-arch ' + a
+ mac_cflags.append(aflag)
+ mac_lflags.append(aflag)
+
+ if self._universal:
+ mac_cflags.append('-isysroot %s' % self._universal)
+ mac_lflags.append('-Wl,-syslibroot,%s' % self._universal)
+
+ cflags.lextend(mac_cflags)
+ cxxflags.lextend(mac_cflags)
+ lflags.lextend(mac_lflags)
+
+ # 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:
+ # Note that qmake doesn't define anything for QtHelp.
+ if mod == "QtCore":
+ defines.append("QT_CORE_LIB")
+ elif mod == "QtGui":
+ defines.append("QT_GUI_LIB")
+ elif mod == "QtMultimedia":
+ defines.append("QT_MULTIMEDIA_LIB")
+ elif mod == "QtNetwork":
+ defines.append("QT_NETWORK_LIB")
+ elif mod == "QtOpenGL":
+ defines.append("QT_OPENGL_LIB")
+ elif mod == "QtScript":
+ defines.append("QT_SCRIPT_LIB")
+ elif mod == "QtScriptTools":
+ defines.append("QT_SCRIPTTOOLS_LIB")
+ elif mod == "QtSql":
+ defines.append("QT_SQL_LIB")
+ elif mod == "QtTest":
+ defines.append("QT_TEST_LIB")
+ elif mod == "QtWebKit":
+ defines.append("QT_WEBKIT_LIB")
+ elif mod == "QtXml":
+ defines.append("QT_XML_LIB")
+ elif mod == "QtXmlPatterns":
+ defines.append("QT_XMLPATTERNS_LIB")
+ elif mod == "phonon":
+ defines.append("QT_PHONON_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",
+ "QtWebKit": "LIBS_WEBKIT"
+ }
+
+ # For Windows: the dependencies between Qt libraries.
+ qdepmap = {
+ "QtAssistant": ("QtNetwork", "QtGui", "QtCore"),
+ "QtGui": ("QtCore", ),
+ "QtHelp": ("QtSql", "QtGui", "QtCore"),
+ "QtMultimedia": ("QtGui", "QtCore"),
+ "QtNetwork": ("QtCore", ),
+ "QtOpenGL": ("QtGui", "QtCore"),
+ "QtScript": ("QtCore", ),
+ "QtScriptTools": ("QtScript", "QtGui", "QtCore"),
+ "QtSql": ("QtCore", ),
+ "QtSvg": ("QtXml", "QtGui", "QtCore"),
+ "QtTest": ("QtGui", "QtCore"),
+ "QtWebKit": ("QtNetwork", "QtGui", "QtCore"),
+ "QtXml": ("QtCore", ),
+ "QtXmlPatterns": ("QtNetwork", "QtCore"),
+ "phonon": ("QtGui", "QtCore"),
+ "QtDesigner": ("QtGui", "QtCore"),
+ "QAxContainer": ("QtGui", "QtCore")
+ }
+
+ # 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 list(wdepmap.keys()):
+ deps.extend(self.optional_list(wdepmap[mod]))
+
+ if mod in list(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 list(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 + version_to_string(self.config.qt_version).replace(".", "")
+
+ 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:
+ if self.config.qt_version < 0x040000 or self._opengl or "QtGui" in self._qt:
+ 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 _add_manifest(self, target=None):
+ """Add the link flags for creating a manifest file.
+ """
+ if target is None:
+ target = "$(TARGET)"
+
+ self.LFLAGS.append("/MANIFEST")
+ self.LFLAGS.append("/MANIFESTFILE:%s.manifest" % target)
+
+ 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" and "shared" in self.config.qt_winconfig.split():
+ if (mname in ("QtCore", "QtDesigner", "QtGui", "QtHelp",
+ "QtMultimedia", "QtNetwork", "QtOpenGL", "QtScript",
+ "QtScriptTools", "QtSql", "QtSvg", "QtTest",
+ "QtWebKit", "QtXml", "QtXmlPatterns", "phonon") 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 = ' '.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:
+ error("Unable to open \"%s\"" % prl_name)
+
+ line = f.readline()
+ while line:
+ line = line.strip()
+ if line and line[0] != "#":
+ eq = line.find("=")
+ if eq > 0 and line[:eq].strip() == "QMAKE_PRL_LIBS":
+ prl_libs = line[eq + 1:].split()
+ 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) == dict:
+ bfname = "dictionary"
+ bdict = filename
+ else:
+ if os.path.isabs(filename):
+ # We appear to be building out of the source tree.
+ self._src_dir = os.path.dirname(filename)
+ bfname = filename
+ else:
+ bfname = os.path.join(self.dir, filename)
+
+ bdict = {}
+
+ try:
+ f = open(bfname, "r")
+ except IOError:
+ error("Unable to open \"%s\"" % bfname)
+
+ line_nr = 1
+ line = f.readline()
+
+ while line:
+ line = line.strip()
+
+ if line and line[0] != "#":
+ eq = line.find("=")
+
+ if eq <= 0:
+ error("\"%s\" line %d: Line must be in the form 'name = value value...'." % (bfname, line_nr))
+
+ bdict[line[:eq].strip()] = line[eq + 1:].strip()
+
+ line_nr = line_nr + 1
+ line = f.readline()
+
+ f.close()
+
+ # Check the compulsory values.
+ for i in ("target", "sources"):
+ try:
+ bdict[i]
+ except KeyError:
+ error("\"%s\" is missing from \"%s\"." % (i, bfname))
+
+ # Get the optional values.
+ for i in ("headers", "moc_headers"):
+ try:
+ bdict[i]
+ except KeyError:
+ bdict[i] = ""
+
+ # Generate the list of objects.
+ if self.generator in ("MSVC", "MSVC.NET", "BMAKE"):
+ ext = ".obj"
+ else:
+ ext = ".o"
+
+ olist = []
+
+ for f in bdict["sources"].split():
+ root, discard = os.path.splitext(f)
+ olist.append(root + ext)
+
+ for f in bdict["moc_headers"].split():
+ 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)
+
+ bdict["objects"] = ' '.join(olist)
+
+ return bdict
+
+ 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 build["objects"].split():
+ mfile.write("\t-%s %s\n" % (self.rm, f))
+
+ for f in build["moc_headers"].split():
+ 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()
+
+ # Make sure the destination directory exists.
+ try:
+ os.makedirs(self.dir)
+ except:
+ pass
+
+ mfname = os.path.join(self.dir, self._makefile)
+
+ try:
+ mfile = open(mfname, "w")
+ except IOError:
+ error("Unable to create \"%s\"" % mfname)
+
+ self.generate_macros_and_rules(mfile)
+ self.generate_target_default(mfile)
+ self.generate_target_install(mfile)
+
+ if self._installs:
+ if type(self._installs) != list:
+ 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 = []
+
+ if not self._debug:
+ cppflags.append("-DNDEBUG")
+
+ 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" % ' '.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" % ' '.join(libs))
+
+ if self._qt:
+ mfile.write("MOC = %s\n" % _quote(self.required_string("MOC")))
+
+ if self._src_dir != self.dir:
+ mfile.write("VPATH = %s\n\n" % self._src_dir)
+
+ # 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) != list:
+ 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)
+
+ for root, dirs, files in os.walk(self._moddir):
+ # Do not recurse into certain directories.
+ for skip in (".svn", "CVS"):
+ if skip in dirs:
+ dirs.remove(skip)
+
+ tail = root[len(self._moddir):]
+ flist = []
+
+ for f in files:
+ if f == "Makefile":
+ continue
+
+ if os.path.isfile(os.path.join(root, 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=None, arch=None):
+ """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, arch)
+
+ self._build = self.parse_build_file(build_file)
+ self._install_dir = install_dir
+ self.static = static
+
+ self._manifest = ("embed_manifest_dll" in self.optional_list("CONFIG"))
+
+ # 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
+ self._limit_exports = not export_all
+
+ # Save the target name for later.
+ self._target = self._build["target"]
+
+ # The name of the module entry point is Python version specific.
+ if self.config.py_version >= 0x030000:
+ self._entry_point = "PyInit_%s" % self._target
+ else:
+ self._entry_point = "init%s" % self._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"
+
+ if self._manifest:
+ self._add_manifest()
+
+ # 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":
+ # 'real_prefix' exists if virtualenv is being used.
+ dl = getattr(sys, 'real_prefix', sys.exec_prefix).split(os.sep)
+
+ if "Python.framework" not in dl:
+ error("SIP requires Python to be built as a framework")
+
+ self.LFLAGS.append("-undefined dynamic_lookup")
+
+ 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,%s' % self._entry_point])
+ 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,%s' % self._entry_point])
+
+ # Force the shared linker if there is one.
+ link_shlib = self.optional_list("LINK_SHLIB")
+
+ if link_shlib:
+ self.LINK.set(link_shlib)
+
+ # This made an appearence in Qt v4.4rc1 and breaks extension modules so
+ # remove it. It was removed at my request but some stupid distros may
+ # have kept it.
+ self.LFLAGS.remove('-Wl,--no-undefined')
+
+ 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.
+ """
+ # Do these first so that it's safe for a sub-class to append additional
+ # commands to the real target, but make sure the default is correct.
+ mfile.write("\nall: $(TARGET)\n")
+ mfile.write("\n$(OFILES): $(HFILES)\n")
+
+ for mf in self._build["moc_headers"].split():
+ 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))
+
+ 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 self._manifest:
+ 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 self._build["objects"].split():
+ 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 = os.path.join(self.dir, self._target + ".def")
+
+ try:
+ dfile = open(defname, "w")
+ except IOError:
+ error("Unable to create \"%s\"" % defname)
+
+ dfile.write("EXPORTS\n")
+ dfile.write("%s=_%s\n" % (self._entry_point, self._entry_point))
+
+ 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: %s; local: *; };' > %s.exp\n" % (self._entry_point, self._target))
+ elif sys.platform[:5] == 'sunos':
+ mfile.write("\t@echo '{ global: %s; local: *; };' > %s.exp\n" % (self._entry_point, self._target))
+ elif sys.platform[:3] == 'aix':
+ mfile.write("\t@echo '#!' >%s.exp" % self._target)
+ mfile.write("; \\\n\t echo '%s' >>%s.exp\n" % (self._entry_point, self._target))
+
+ mfile.write("\t$(LINK) $(LFLAGS) -o $(TARGET) $(OFILES) $(LIBS)\n")
+
+ 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)
+
+ if self._manifest and not self.static:
+ mfile.write("\t-%s $(TARGET).manifest\n" % self.rm)
+
+ # 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 __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=None, arch=None, prot_is_public=0):
+ """Initialise an instance of a SIP generated module Makefile.
+
+ prot_is_public is set if "protected" is to be redefined as "public".
+ If the platform's C++ ABI allows it this can significantly reduce the
+ size of the generated code.
+
+ For all other arguments see ModuleMakefile.
+ """
+ ModuleMakefile.__init__(self, configuration, build_file, install_dir,
+ static, console, qt, opengl, threaded, warnings, debug, dir,
+ makefile, installs, strip, export_all, universal, arch)
+
+ self._prot_is_public = prot_is_public
+
+ def finalise(self):
+ """Finalise the macros for a SIP generated module Makefile.
+ """
+ if self._prot_is_public:
+ self.DEFINES.append('SIP_PROTECTED_IS_PUBLIC')
+ self.DEFINES.append('protected=public')
+
+ 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=None, arch=None):
+ """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, arch)
+
+ self._install_dir = install_dir
+
+ self._manifest = ("embed_manifest_exe" in self.optional_list("CONFIG"))
+ self._target = None
+
+ 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.
+ """
+ # The name of the executable.
+ self._target, _ = os.path.splitext(source)
+
+ if sys.platform in ("win32", "cygwin"):
+ exe = self._target + ".exe"
+ else:
+ exe = self._target
+
+ self.ready()
+
+ # The command line.
+ build = []
+
+ build.append(self.required_string("CXX"))
+
+ for a in self._arch.split():
+ build.append('-arch ' + a)
+
+ 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 lf.split():
+ # 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 f[-4:].lower() != ".obj":
+ blflags.append(f)
+
+ lflags = blflags
+
+ build.extend(lflags)
+
+ build.extend(self.optional_list("LIBS"))
+
+ if self.generator == "BMAKE":
+ build.append(source)
+
+ return (exe, ' '.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._manifest:
+ self._add_manifest(self._target)
+
+ 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.
+ """
+ # Do these first so that it's safe for a sub-class to append additional
+ # commands to the real target, but make sure the default is correct.
+ mfile.write("\nall: $(TARGET)\n")
+ mfile.write("\n$(OFILES): $(HFILES)\n")
+
+ for mf in self._build["moc_headers"].split():
+ root, _ = os.path.splitext(mf)
+ cpp = "moc_" + root + ".cpp"
+
+ if self._src_dir != self.dir:
+ mf = os.path.join(self._src_dir, mf)
+
+ mfile.write("\n%s: %s\n" % (cpp, mf))
+ mfile.write("\t$(MOC) -o %s %s\n" % (cpp, mf))
+
+ 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")
+ 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")
+
+ if self._manifest:
+ mfile.write("\tmt -nologo -manifest $(TARGET).manifest -outputresource:$(TARGET);1\n")
+
+ 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)
+
+ if self._manifest:
+ mfile.write("\t-%s $(TARGET).manifest\n" % self.rm)
+
+
+def _quote(s):
+ """Return a string surrounded by double quotes it if contains a space.
+
+ s is the string.
+ """
+ if s.find(" ") >= 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 = l.split()
+ if len(wl) >= 3 and wl[0] == "#define":
+ if need_num and wl[1] == numdefine:
+ v = wl[2]
+
+ if v[0:2] == "0x":
+ vers = int(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 = ' '.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(cdict, 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 = list(cdict.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 = cdict[k]
+ vtype = type(val)
+ delim = None
+
+ if val is None:
+ val = "None"
+ elif vtype == list:
+ val = ' '.join(val)
+ delim = "'"
+ elif vtype == int:
+ if k.find("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)
+ delim = "'"
+
+ if delim:
+ if "'" in val:
+ delim = "'''"
+
+ val = delim + val + delim
+
+ content = content + " '" + k + "':" + (" " * (width - len(k) + 2)) + val.replace("\\", "\\\\")
+
+ 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 = list(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 = ","
+
+ val = macros[c]
+ if "'" in val:
+ delim = "'''"
+ else:
+ delim = "'"
+
+ k = "'" + c + "':"
+ content = content + " %-*s %s%s%s%s\n" % (1 + width + 2, k, delim, val.replace("\\", "\\\\"), delim, 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) == dict:
+ 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 line.find(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 = list(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 msg.split():
+ 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 = oride.find("+=")
+
+ if name_end >= 0:
+ prefix = "+"
+ val_start = name_end + 2
+ else:
+ name_end = oride.find("=")
+
+ 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:
+ # If this file is conditional then don't raise an error.
+ if self.cond_fname == filename:
+ return
+
+ error("Unable to open %s" % filename)
+
+ 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 = line.strip()
+
+ # Ignore comments.
+ if line and line[0] != "#":
+ assstart = line.find("+")
+ if assstart > 0 and line[assstart + 1] == '=':
+ adding = True
+ assend = assstart + 1
+ else:
+ adding = False
+ assstart = line.find("=")
+ assend = assstart
+
+ if assstart > 0:
+ lhs = line[:assstart].strip()
+ rhs = line[assend + 1:].strip()
+
+ # Remove the escapes for any quotes.
+ rhs = rhs.replace(r'\"', '"').replace(r"\'", "'")
+
+ if adding and rhs != "":
+ orig_rhs = raw.get(lhs)
+ if orig_rhs is not None:
+ rhs = orig_rhs + " " + rhs
+
+ 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 list(raw.keys()):
+ # Strip any prefix.
+ if lhs.find(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 = rhs.find("$$(")
+ mstart = rhs.find("$$")
+
+ 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 list(properties.keys()):
+ error("%s: property '%s' is not defined." % (filename, lhs))
+
+ value = properties[lhs]
+ else:
+ try:
+ value = raw[lhs]
+ except KeyError:
+ # We used to treat this as an error, but Qt v4.3.0 has at
+ # least one case that refers to an undefined macro. If
+ # qmake handles it then this must be the correct behaviour.
+ value = ""
+
+ rhs = rhs[:mstart] + value + rhs[mend:]
+ estart = rhs.find("$$(")
+ mstart = rhs.find("$$")
+
+ # Expand any POSIX style environment variables.
+ pleadin = ["$$(", "$("]
+
+ for pl in pleadin:
+ estart = rhs.find(pl)
+
+ if estart >= 0:
+ nstart = estart + len(pl)
+ break
+ else:
+ estart = -1
+
+ while estart >= 0:
+ eend = rhs[nstart:].find(")")
+
+ 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 = rhs.find(pl)
+
+ if estart >= 0:
+ nstart = estart + len(pl)
+ break
+ else:
+ estart = -1
+
+ # Expand any Windows style environment variables.
+ estart = rhs.find("%")
+
+ while estart >= 0:
+ eend = rhs[estart + 1:].find("%")
+
+ 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 = rhs.find("%")
+
+ refined[reflhs] = rhs
+
+ # Handle the user overrides.
+ for lhs in list(orides.keys()):
+ rhs = refined[lhs]
+ oride = orides[lhs]
+
+ if oride.find("+") == 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, use_arch=''):
+ """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.
+ use_arch is the MacOS/X architecture to invoke python with.
+
+ 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":
+ # The installation of MacOS's python is a mess that changes from
+ # version to version and where sys.executable is useless.
+
+ if gui:
+ exe = "pythonw"
+ else:
+ exe = "python"
+
+ version = sys.version_info
+ exe = "%s%d.%d" % (exe, version[0], version[1])
+
+ if use_arch:
+ # Note that this may not work with the "standard" interpreter but
+ # should with the "pythonX.Y" version.
+ exe = "arch -%s %s" % (use_arch, exe)
+
+ wf.write("#!/bin/sh\n")
+ wf.write("exec %s %s ${1+\"$@\"}\n" % (exe, script))
+ else:
+ wf.write("#!/bin/sh\n")
+ 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