# This script generates the PyQt configuration and generates the Makefiles.
#
# Copyright (c) 2006
# 	Riverbank Computing Limited <info@riverbankcomputing.co.uk>
# 
# This file is part of PyQt.
# 
# This copy of PyQt is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2, or (at your option) any later
# version.
# 
# PyQt is supplied in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
# details.
# 
# You should have received a copy of the GNU General Public License along with
# PyQt; see the file LICENSE.  If not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


import sys
import os
import string
import glob
import getopt
import shutil

import sipconfig


# Initialise the globals.
pyqt_version = 0x040001
pyqt_version_str = "4.0.1"

sip_min_version = 0x040404

qt_version = 0
qt_edition = ""
qt_dir = None
qt_incdir = None
qt_libdir = None
qt_bindir = None
qt_datadir = None
qt_xfeatures = None
qt_winconfig = ""
qt_lib = None
qt_framework = 0

qt_sip_flags = []

pyqt_modules = []

# Get the SIP configuration.
sipcfg = sipconfig.Configuration()

# Command line options.
opt_qmake = None
opt_pyqtbindir = sipcfg.default_bin_dir
opt_pyqtmoddir = os.path.join(sipcfg.default_mod_dir, "PyQt4")
opt_pyqtsipdir = os.path.join(sipcfg.default_sip_dir, "PyQt4")
opt_static = 0
opt_debug = 0
opt_concat = 0
opt_split = 1
opt_tracing = 0
opt_verbose = 0

opt_vendorcheck = 0
opt_vendincdir = sipcfg.py_inc_dir
opt_vendlibdir = sipcfg.py_lib_dir


def usage(rcode = 2):
    """Display a usage message and exit.

    rcode is the return code passed back to the calling process.
    """
    if opt_qmake:
        def_qmake = opt_qmake
    else:
        def_qmake = "none"

    print "Usage:"
    print "    python configure.py [-h] [-b dir] [-c] [-d dir] [-i] [-j #] [-k] [-l dir] [-m dir] [-q exe] [-r] [-u] [-v dir] [-w] option=value option+=value ..."
    print "where:"
    print "  -h      display this help message"
    print "  -b dir  where pyuic4, pyrcc4 and pylupdate4 will be installed [default %s]" % opt_pyqtbindir
    print "  -c      concatenate each module's C++ source files"
    print "  -d dir  where the PyQt4 modules will be installed [default %s]" % opt_pyqtmoddir
    print "  -i      enable checking of signed interpreters using the VendorID package [default disabled]"
    print "  -j n    split the concatenated C++ source files into n pieces [default 1]"
    print "  -k      build the PyQt4 modules as static libraries"
    print "  -l dir  the directory containing the VendorID header file [default %s]" % opt_vendincdir
    print "  -m dir  the directory containing the VendorID library [default %s]" % opt_vendlibdir
    print "  -q exe  the pathname of qmake [default %s]" % def_qmake
    print "  -r      generate code with tracing enabled [default disabled]"
    print "  -u      build with debugging symbols"
    print "  -v dir  where the PyQt4 .sip files will be installed [default %s]" % opt_pyqtsipdir
    print "  -w      don't suppress compiler output during configuration"

    sys.exit(rcode)


class ConfigurePyQt4:
    """This class defines the methods to configure PyQt4.
    """
    def qt_version_tags(self):
        """Get the versions tags for the configuration.

        Returns a dictionary of versions and corresponding tags.
        """
        return {
            0x040101: None,
            0x040102: "Qt_4_1_1",
            0x040103: "Qt_4_1_2",
            0x050000: "Qt_4_1_3"
        }

    def check_modules(self):
        pyqt_modules.append("QtCore")

        check_module("QtGui", "qwidget.h", "QWidget()")
        check_module("QtNetwork", "qhostaddress.h", "QHostAddress()")
        check_module("QtOpenGL", "qgl.h", "QGLWidget()")
        check_module("QtSql", "qsqldatabase.h", "QSqlDatabase()")
        check_module("QtSvg", "qsvgwidget.h", "QSvgWidget()")
        check_module("QtXml", "qdom.h", "QDomDocument()")
        check_module("QtAssistant", "qassistantclient.h", "QAssistantClient(\"foo\")")

    def code(self):
        # Note that the order of the dependent modules is important.

        if opt_vendorcheck:
            generate_code("QtCore", ["QtCore"], opt_vendincdir, opt_vendlibdir, "vendorid")
        else:
            generate_code("QtCore", ["QtCore"])

        if "QtGui" in pyqt_modules:
            generate_code("QtGui", ["QtGui", "QtCore"])

        if "QtNetwork" in pyqt_modules:
            generate_code("QtNetwork", ["QtNetwork", "QtCore"])

        if "QtOpenGL" in pyqt_modules:
            generate_code("QtOpenGL", ["QtOpenGL", "QtGui", "QtCore"])

        if "QtSvg" in pyqt_modules:
            generate_code("QtSvg", ["QtSvg", "QtGui", "QtCore"])

        if "QtSql" in pyqt_modules:
            generate_code("QtSql", ["QtSql", "QtGui", "QtCore"])

        if "QtXml" in pyqt_modules:
            generate_code("QtXml", ["QtXml", "QtCore"])

        if "QtAssistant" in pyqt_modules:
            generate_code("QtAssistant", ["QtAssistant", "QtNetwork", "QtGui", "QtCore"])

        # Generate the consolidated module.
        sipconfig.inform("Creating Qt consolidated module...")
        f = open("Qt.py", "w")

        for m in pyqt_modules:
            f.write("from PyQt4.%s import *\n" % m)

        f.close()

    def module_installs(self):
        return ["__init__.py", "Qt.py", "pyqtconfig.py"]

    def tools(self):
        tool = []

        sipconfig.inform("Creating elementtree Makefile...")

        makefile = sipconfig.PythonModuleMakefile(
            configuration=sipcfg,
            dstdir=os.path.join(opt_pyqtmoddir, "elementtree"),
            dir="elementtree"
        )

        makefile.generate()
        tool.append("elementtree")

        # Create the pyuic4 wrapper.  Use the GUI version on MacOS (so that
        # previews work properly and normal console use will work anyway), but
        # not on Windows (so that normal console use will work).
        sipconfig.inform("Creating pyuic4 wrapper...")

        uicdir=os.path.join(opt_pyqtmoddir, "uic")
        wrapper = sipconfig.create_wrapper(os.path.join(uicdir, "pyuic.py"), os.path.join("pyuic", "pyuic4"), (sys.platform == "darwin"))

        sipconfig.inform("Creating pyuic4 Makefile...")

        makefile = sipconfig.PythonModuleMakefile(
            configuration=sipcfg,
            dstdir=uicdir,
            srcdir="uic",
            dir="pyuic",
            installs=[[os.path.basename(wrapper), opt_pyqtbindir]]
        )

        makefile.generate()
        tool.append("pyuic")

        if "QtXml" in pyqt_modules:
            sipconfig.inform("Creating pylupdate4 Makefile...")

            makefile = sipconfig.ProgramMakefile(
                configuration=sipcfg,
                build_file="pylupdate.sbf",
                dir="pylupdate",
                install_dir=opt_pyqtbindir,
                console=1,
                qt=["QtCore", "QtGui", "QtXml"],
                debug=opt_debug,
                warnings=1
            )

            makefile.generate()
            tool.append("pylupdate")

            sipconfig.inform("Creating pyrcc4 Makefile...")

            makefile = sipconfig.ProgramMakefile(
                configuration=sipcfg,
                build_file="pyrcc.sbf",
                dir="pyrcc",
                install_dir=opt_pyqtbindir,
                console=1,
                qt=["QtCore", "QtXml"],
                debug=opt_debug,
                warnings=1
            )

            makefile.generate()
            tool.append("pyrcc")
        else:
            sipconfig.inform("pylupdate4 and pyrcc4 will not be built because the Qt XML module is missing.")

        return tool


def inform_user():
    """Tell the user the option values that are going to be used.
    """
    if qt_edition:
        edstr = qt_edition + " edition "
    else:
        edstr = ""

    sipconfig.inform("Qt v%s %sis being used." % (sipconfig.version_to_string(qt_version), edstr))

    if sys.platform == "darwin" and qt_framework:
        sipconfig.inform("Qt is built as a framework.")

    sipconfig.inform("SIP %s is being used." % sipcfg.sip_version_str)
    sipconfig.inform("The Qt header files are in %s." % qt_incdir)
    sipconfig.inform("The %s Qt library is in %s." % (qt_lib, qt_libdir))
    sipconfig.inform("The Qt binaries are in %s." % qt_bindir)
    sipconfig.inform("The Qt mkspecs directory is in %s." % qt_datadir)
    sipconfig.inform("These PyQt modules will be built: %s." % string.join(pyqt_modules))

    sipconfig.inform("The PyQt modules will be installed in %s." % opt_pyqtmoddir)
    sipconfig.inform("The PyQt .sip files will be installed in %s." % opt_pyqtsipdir)

    sipconfig.inform("pyuic4, pyrcc4 and pylupdate4 will be installed in %s." % opt_pyqtbindir)

    if opt_vendorcheck:
        sipconfig.inform("PyQt will only be usable with signed interpreters.")


def create_config(module, template, macros):
    """Create the PyQt configuration module so that it can be imported by build
    scripts.

    module is the module file name.
    template is the template file name.
    macros is the dictionary of platform specific build macros.
    """
    sipconfig.inform("Creating %s..." % module)

    content = {
        "pyqt_config_args":   sys.argv[1:],
        "pyqt_version":       pyqt_version,
        "pyqt_version_str":   pyqt_version_str,
        "pyqt_bin_dir":       opt_pyqtbindir,
        "pyqt_mod_dir":       opt_pyqtmoddir,
        "pyqt_sip_dir":       opt_pyqtsipdir,
        "pyqt_modules":       pyqt_modules,
        "pyqt_sip_flags":     qt_sip_flags,
        "qt_version":         qt_version,
        "qt_edition":         qt_edition,
        "qt_winconfig":       qt_winconfig,
        "qt_framework":       qt_framework,
        "qt_threaded":        1,
        "qt_dir":             qt_dir,
        "qt_data_dir":        qt_datadir,
        "qt_inc_dir":         qt_incdir,
        "qt_lib_dir":         qt_libdir
    }

    sipconfig.create_config_module(module, template, content, macros)


def run_command(cmd):
    """Run a command and display the output if verbose mode is enabled.

    cmd is the command to run.
    """
    if opt_verbose:
        sys.stdout.write(cmd + "\n")

    fin, fout = os.popen4(cmd)

    # Read stdout and stderr until there is no more output.
    lout = fout.readline()
    while lout:
        if opt_verbose:
            sys.stdout.write(lout)

        lout = fout.readline()

    fin.close()
    fout.close()

    try:
        os.wait()
    except AttributeError:
        pass


def remove_file(fname):
    """Remove a file which may or may not exist.

    fname is the name of the file.
    """
    try:
        os.remove(fname)
    except OSError:
        pass


def compile_qt_program(name, mname):
    """Compile a simple Qt application.

    name is the name of the single source file.
    mname is the name of the Qt module.

    Returns the name of the executable suitable for running or None if it
    wasn't created.
    """
    opengl = (mname == "QtOpenGL")

    makefile = sipconfig.ProgramMakefile(sipcfg, console=1, qt=[mname], warnings=0, opengl=opengl, debug=opt_debug)

    exe, build = makefile.build_command(name)

    # Make sure the executable file doesn't exist.
    remove_file(exe)
    run_command(build)

    if not os.access(exe, os.X_OK):
        return None

    if sys.platform != "win32":
        exe = "./" + exe

    return exe


def check_vendorid():
    """See if the VendorID library and include file can be found.
    """
    global opt_vendorcheck

    if opt_vendorcheck:
        if os.access(os.path.join(opt_vendincdir, "vendorid.h"), os.F_OK):
            if glob.glob(os.path.join(opt_vendlibdir, "*vendorid*")):
                sipconfig.inform("The VendorID package was found.")
            else:
                opt_vendorcheck = 0
                sipconfig.inform("The VendorID library could not be found in %s and so signed interpreter checking will be disabled. If the VendorID library is installed then use the -m argument to explicitly specify the correct directory." % opt_vendlibdir)
        else:
            opt_vendorcheck = 0
            sipconfig.inform("vendorid.h could not be found in %s and so signed interpreter checking will be disabled. If the VendorID package is installed then use the -l argument to explicitly specify the correct directory." % opt_vendincdir)


def check_module(mname, incfile, ctor):
    """See if a module can be built and, if so, add it to the global list of
    modules.

    mname is the name of the module.
    incfile is the name of the include file needed for the test.
    ctor is the C++ constructor of the class being used for the test.
    """
    # Check the module's main .sip file exists.
    if os.access(os.path.join("sip", mname, mname + "mod.sip"), os.F_OK):
        sipconfig.inform("Checking to see if the %s module should be built..." % mname)

        if check_class(incfile, ctor, mname):
            pyqt_modules.append(mname)


def check_class(incfile, ctor, mname):
    """Return non-zero if a class is available.

    incfile is the name of the include file needed for the test.
    ctor is the C++ constructor of the class.
    mname is the name of the module.
    """
    # We use a module specific name to avoid a potential problem on Windows
    # where the operating system doesn't delete previous tests quickly enough.
    cfgtest = "cfgtest_%s.cpp" % mname

    f = open(cfgtest, "w")

    f.write("""#include <%s>

int main(int, char **)
{
    new %s;
}
""" % (incfile, ctor))

    f.close()

    return compile_qt_program(cfgtest, mname)


def set_sip_flags(pyqt):
    """Set the SIP platform, version and feature flags.

    pyqt is the configuration instance.
    """
    # If we don't check for signed interpreters, we exclude the 'VendorID'
    # feature
    if not opt_vendorcheck:
        qt_sip_flags.append("-x")
        qt_sip_flags.append("VendorID")

    # Handle the platform tag.
    if sys.platform == "win32":
        plattag = "WS_WIN"
    elif sys.platform == "darwin":
        if "__USE_WS_X11__" in sipcfg.build_macros()["DEFINES"]:
            plattag = "WS_X11"
        else:
            plattag = "WS_MACX"
    else:
        plattag = "WS_X11"

    qt_sip_flags.append("-t")
    qt_sip_flags.append(plattag)

    # Handle the Qt version tag.
    verstag = sipconfig.version_to_sip_tag(qt_version, pyqt.qt_version_tags(), "Qt")

    # Handle any feature flags.
    for xf in qt_xfeatures:
        qt_sip_flags.append("-x")
        qt_sip_flags.append(xf)

    if verstag:
        qt_sip_flags.append("-t")
        qt_sip_flags.append(verstag)


def generate_code(mname, qt_mods, extra_include_dir=None, extra_lib_dir=None, extra_lib=None):
    """Generate the code for a module.

    mname is the name of the module.
    qt_mods is the list of Qt modules that are needed.
    extra_include_dir is an optional directory to add to the list of include
    directories.
    extra_lib_dir is an optional directory to add to the list of library
    directories.
    extra_lib is an optional library to add to the list of libraries.
    """
    sipconfig.inform("Generating the C++ source for the %s module..." % mname)

    try:
        shutil.rmtree(mname)
    except:
        pass

    try:
        os.mkdir(mname)
    except:
        sipconfig.error("Unable to create the %s directory." % mname)

    # Build the SIP command line.
    argv = ['"' + sipcfg.sip_bin + '"']

    argv.extend(qt_sip_flags)

    if opt_concat:
        argv.append("-j")
        argv.append(str(opt_split))

    if opt_tracing:
        argv.append("-r")

    argv.append("-c")
    argv.append(mname)

    buildfile = os.path.join(mname, mname + ".sbf")
    argv.append("-b")
    argv.append(buildfile)

    argv.append("-I")
    argv.append("sip")

    # SIP assumes POSIX style path separators.
    argv.append(string.join(["sip", mname, mname + "mod.sip"], "/"))

    cmd = string.join(argv)

    if opt_verbose:
        sys.stdout.write(cmd + "\n")

    os.system(cmd)

    # Check the result.
    if not os.access(buildfile, os.F_OK):
        sipconfig.error("Unable to create the C++ code.")

    # Generate the Makefile.
    sipconfig.inform("Creating the Makefile for the %s module..." % mname)

    installs = []
    sipfiles = []

    for s in glob.glob("sip/" + mname + "/*.sip"):
        sipfiles.append(os.path.join("..", "sip", mname, os.path.basename(s)))

    installs.append([sipfiles, os.path.join(opt_pyqtsipdir, mname)])

    opengl = (mname == "QtOpenGL")

    makefile = sipconfig.SIPModuleMakefile(
        configuration=sipcfg,
        build_file=mname + ".sbf",
        dir=mname,
        install_dir=opt_pyqtmoddir,
        installs=installs,
        qt=qt_mods,
        opengl=opengl,
        warnings=1,
        static=opt_static,
        debug=opt_debug
    )

    if extra_include_dir:
        makefile.extra_include_dirs.append(extra_include_dir)

    if extra_lib_dir:
        makefile.extra_lib_dirs.append(extra_lib_dir)

    if extra_lib:
        makefile.extra_libs.append(extra_lib)

    makefile.generate()


def check_license():
    """Handle the validation of the PyQt license.
    """
    try:
        import license
        ltype = license.LicenseType
        lname = license.LicenseName

        try:
            lfile = license.LicenseFile
        except AttributeError:
            lfile = None
    except ImportError:
        ltype = None

    if ltype is None:
        ltype = "GPL"
        lname = "GNU General Public License"
        lfile = None

    sipconfig.inform("This is the %s version of PyQt %s (licensed under the %s) for Python %s on %s." % (ltype, pyqt_version_str, lname, string.split(sys.version)[0], sys.platform))

    # Common checks.
    try:
        qted = qt_edition
    except AttributeError:
        qted = None

    if qted and ltype != "internal":
        if (qted == "free" and ltype != "GPL") or (qted != "free" and ltype == "GPL"):
            sipconfig.error("This version of PyQt and the %s edition of Qt have incompatible licenses." % qted)

    # Confirm the license.
    print
    print "Type 'L' to view the license."
    print "Type 'yes' to accept the terms of the license."
    print "Type 'no' to decline the terms of the license."
    print

    while 1:
        try:
            resp = raw_input("Do you accept the terms of the license? ")
        except:
            resp = ""

        resp = string.lower(string.strip(resp))

        if resp == "yes":
            break

        if resp == "no":
            sys.exit(0)

        if resp == "l":
            os.system("more LICENSE")

    # If there should be a license file then check it is where it should be.
    if lfile:
        if os.access(os.path.join("sip", lfile), os.F_OK):
            sipconfig.inform("Found the license file %s." % lfile)
        else:
            sipconfig.error("Please copy the license file %s to the sip directory." % lfile)


def get_build_macros(overrides):
    """Return the dictionary of platform specific build macros from the Qt
    installation.  Return None if any of the overrides was invalid.

    overrides is a list of macros overrides from the user.
    """
    # Get the name of the qmake configuration file to take the macros from.
    if "QMAKESPEC" in os.environ.keys():
        fname = os.environ["QMAKESPEC"]

        if not os.path.dirname(fname):
            fname = os.path.join(qt_datadir, "mkspecs", fname)
    else:
        fname = os.path.join(qt_datadir, "mkspecs", "default")

    fname = os.path.join(fname, "qmake.conf")

    if not os.access(fname, os.F_OK):
        sipconfig.error("Unable to find the qmake configuration file %s. Use the QMAKESPEC environment variable to specify the correct platform." % fname)

    # Add the Qt specific macros to the default.
    names = sipcfg.build_macros().keys()
    names.append("INCDIR_QT")
    names.append("LIBDIR_QT")
    names.append("MOC")

    properties = {
        "QT_INSTALL_BINS":      qt_bindir,
        "QT_INSTALL_HEADERS":   qt_incdir,
        "QT_INSTALL_LIBS":      qt_libdir
    }

    return sipconfig.parse_build_macros(fname, names, overrides, properties)


def qt_core_header(incdir, fname):
    """Find a QtCore header file.

    incdir is the directory expected to contain the file.
    fname is the base name of the file.

    Returns the full path of the file or None if it wasn't found.
    """
    pn = os.path.join(incdir, fname)

    try:
        sbuf = os.stat(pn)
    except OSError:
        sbuf = None

    if sbuf:
        # In some Qt4 installations the public header files just have a single
        # line that #includes the real file in the src directory.  Rather than
        # parse #include directives we just look at the size of the file and
        # assume it contains a #include if it seems too small to contain
        # anything useful.
        if sbuf.st_size < 100:
            sn = os.path.join(qt_dir, "src", "corelib", "global", fname)

            if os.access(sn, os.F_OK):
                pn = sn
    else:
        pn = None

    return pn


def qt_inc_dir(qtmod):
    """Get the directory containing the header files for a particular Qt
    module.

    qtmod is the name of the Qt module.

    Returns the directory name.
    """
    # QLibraryInfo doesn't report the correct directories when built as a
    # Darwin framework.
    if qt_framework:
        incdir = os.path.join(qt_libdir, qtmod + ".framework", "Headers")
    else:
        incdir = os.path.join(qt_incdir, qtmod)

    return incdir


def check_qt_installation(macros):
    """Check the Qt installation and get the version number and edition and
    return the configuration instance.

    macros is the dictionary of build macros.
    """
    # Work out how Qt was built on MacOS.
    if sys.platform == "darwin":
        if os.access(os.path.join(qt_libdir, "QtCore.framework"), os.F_OK):
            global qt_framework
            qt_framework = 1

    # Get the Makefile generator.
    generator = macros["MAKEFILE_GENERATOR"]

    # Check the Qt header files have been installed.
    incdir = qt_inc_dir("QtCore")

    qglobal = qt_core_header(incdir, "qglobal.h")

    if not qglobal:
        sipconfig.error("qglobal.h could not be found in %s." % incdir)

    # Get the Qt version number.
    global qt_version

    qt_version, ignore = sipconfig.read_version(qglobal, "Qt", "QT_VERSION")

    if qt_version < 0x040100:
        sipconfig.error("PyQt v4 requires Qt v4.1.0 or later.")

    # Try and work out which edition it is.
    global qt_edition

    # See if it is the open source edition.
    qconfig = qt_core_header(incdir, "qconfig.h")

    if not qconfig:
        sipconfig.error("qconfig.h could not be found in %s." % incdir)

    f = open(qconfig)
    l = f.readline()

    while l:
        wl = string.split(l)

        if len(wl) == 3 and wl[0] == "#define":
            name_val = wl[1:]
        elif len(wl) == 4 and wl[0] == "#" and wl[1] == "define":
            name_val = wl[2:]
        else:
            name_val = None

        if name_val and name_val[0] == "QT_EDITION" and name_val[1] == "QT_EDITION_OPENSOURCE":
            qt_edition = "free"
            break

        l = f.readline()

    f.close()

    if not qt_edition:
        # Work out which commercial edition it is.
        if not os.access(qt_inc_dir("QtGui"), os.F_OK):
            qt_edition = "Console"
        elif not os.access(qt_inc_dir("QtXml"), os.F_OK):
            qt_edition = "Desktop Light"
        else:
            qt_edition = "Desktop"

    if sys.platform == "win32":
        # Work out how Qt was built on Windows.

        global qt_winconfig

        try:
            f = open(os.path.join(qt_dir, ".qtwinconfig"), "r")
        except IOError:
            f = None

        if f:
            cfg = f.readline()
            f.close()

            val = string.find(cfg, "=")

            if val >= 0:
                qt_winconfig = string.strip(cfg[val + 1:])
        else:
            # Assume it was built as a DLL.
            qt_winconfig = "shared"

    # Determine the Qt library to link against.
    resolve_qt4_library(generator)

    # We haven't yet factored out sipconfig's knowledge of how to build Qt
    # binaries and it is expecting to find these in the configuration when it
    # generates the Makefiles.
    sipcfg.qt_version = qt_version
    sipcfg.qt_edition = qt_edition
    sipcfg.qt_winconfig = qt_winconfig
    sipcfg.qt_framework = qt_framework
    sipcfg.qt_threaded = 1
    sipcfg.qt_dir = qt_dir
    sipcfg.qt_lib_dir = qt_libdir

    return ConfigurePyQt4()


def resolve_qt4_library(generator):
    """Check that the Qt v4 library can be found.

    generator is the name of the Makefile generator.
    """
    global qt_lib

    qt_lib = "QtCore"

    if opt_debug and sys.platform != "win32":
        qt_lib = qt_lib + "_debug"

    if not is_qt_library(generator, qt_lib):
        sipconfig.error("The %s Qt library could not be found in %s." % (qt_lib, qt_libdir))


def is_qt_library(generator, lib):
    """See if a particular Qt library is installed.

    generator is the name of the Makefile generator.
    lib is the name of the library.
    """
    if generator in ("MSVC", "MSVC.NET", "BMAKE"):
        lpatts = [lib + "[0-9]*.lib", lib + ".lib"]
    elif generator == "MINGW":
        lpatts = ["lib" + lib + "[0-9]*.a"]
    elif sys.platform == "darwin" and qt_framework:
        lpatts = [lib + ".framework"]
    else:
        lpatts = ["lib" + lib + ".*"]

    for lpatt in lpatts:
        lmatch = glob.glob(os.path.join(qt_libdir, lpatt))

        if lmatch:
            return lmatch

    return []


def get_qt_configuration():
    """Set the qt_dir, qt_incdir, qt_libdir, qt_bindir, qt_datadir and
    qt_xfeatures globals for the Qt installation.
    """
    sipconfig.inform("Determining the layout of your Qt installation...")

    if not opt_qmake:
        sipconfig.error("Make sure you have a working Qt v4 qmake on your PATH or use the -q argument to explicitly specify a working Qt v4 qmake.")

    # The file names we will use to get the directory information.
    app = "qtdirs"
    pro_file = app + ".pro"
    make_file = app + ".mk"
    make_target = ""
    cpp_file = app + ".cpp"
    out_file = app + ".out"

    if sys.platform == "win32":
        exe_file = os.path.join("release", app + ".exe")
        make_target = " release"
    elif sys.platform == "darwin":
        exe_file = os.path.join(app + ".app", "Contents", "MacOS", app)
    else:
        exe_file = os.path.join(".", app)

    # Generate the qmake project file.
    f = open(pro_file, "w")

    f.write(
"""QT = core
TARGET = %s
SOURCES = %s
""" % (app, cpp_file))

    f.close()

    # Generate the source code.
    f = open(cpp_file, "w")

    f.write(
"""#include <QFile>
#include <QLibraryInfo>
#include <QTextStream>

int main(int, char **)
{
    QFile outf("%s");

    if (!outf.open(QIODevice::WriteOnly|QIODevice::Truncate|QIODevice::Text))
        return 1;

    QTextStream out(&outf);

    out << QLibraryInfo::location(QLibraryInfo::PrefixPath) << '\\n';
    out << QLibraryInfo::location(QLibraryInfo::HeadersPath) << '\\n';
    out << QLibraryInfo::location(QLibraryInfo::LibrariesPath) << '\\n';
    out << QLibraryInfo::location(QLibraryInfo::BinariesPath) << '\\n';
    out << QLibraryInfo::location(QLibraryInfo::DataPath) << '\\n';

#if defined(QT_NO_ACCESSIBILITY)
    out << "PyQt_Accessibility\\n";
#endif

#if defined(QT_NO_SESSIONMANAGER)
    out << "PyQt_SessionManager\\n";
#endif

#if defined(QT_NO_STATUSTIP)
    out << "PyQt_StatusTip\\n";
#endif

#if defined(QT_NO_TOOLTIP)
    out << "PyQt_ToolTip\\n";
#endif

#if defined(QT_NO_WHATSTHIS)
    out << "PyQt_WhatsThis\\n";
#endif

#if !defined(QT3_SUPPORT) || QT_VERSION >= 0x040200
    out << "PyQt_NoPrintRangeBug\\n";
#endif

    return 0;
}
""" % out_file)

    f.close()

    # Create the makefile, first making sure it doesn't already exist.
    remove_file(make_file)
    run_command("%s -o %s %s" % (opt_qmake, make_file, pro_file))

    if not os.access(make_file, os.F_OK):
        sipconfig.error("%s failed to create a makefile. Make sure you have a working Qt v4 qmake on your PATH or use the -q argument to explicitly specify a working Qt v4 qmake." % opt_qmake)

    # Try and work out the name of make.
    if sipcfg.platform.startswith("win32-msvc"):
        make = "nmake"
    elif sipcfg.platform == "win32-borland":
        make = "bmake"
    elif sipcfg.platform == "win32-g++":
        make = "mingw32-make"
    else:
        make = "make"

    # Create the executable, first making sure it doesn't exist.
    remove_file(exe_file)
    run_command("%s -f %s%s" % (make, make_file, make_target))

    if not os.access(exe_file, os.X_OK):
        sipconfig.error("Failed to create %s. Make sure you have a working Qt v4 qmake on your PATH or use the -q argument to explicitly specify a working Qt v4 qmake." % exe_file)

    # Create the output file, first making sure it doesn't exist.
    remove_file(out_file)
    run_command(exe_file)

    if not os.access(out_file, os.F_OK):
        sipconfig.error("%s failed to create %s. Make sure your Qt v4 installation is correct." % (exe_file, out_file))

    # Read the directories.
    f = open(out_file, "r")
    lines = f.read().strip().split("\n")
    f.close()

    global qt_dir, qt_incdir, qt_libdir, qt_bindir, qt_datadir, qt_xfeatures

    qt_dir = lines[0]
    qt_incdir = lines[1]
    qt_libdir = lines[2]
    qt_bindir = lines[3]
    qt_datadir = lines[4]
    qt_xfeatures = lines[5:]


def main(argv):
    """Create the configuration module module.

    argv is the list of command line arguments.
    """
    # Check SIP is new enough.
    if sipcfg.sip_version_str[:8] != "snapshot":
        if sipcfg.sip_version < sip_min_version:
            sipconfig.error("This version of PyQt requires SIP v%s or later" % sipconfig.version_to_string(sip_min_version))

    # The process to find the Qt installation to use is driven by qmake, so
    # first try and find a default qmake, ie. the first on the path.
    try:
        path = os.environ["PATH"]
    except KeyError:
        path = ""

    if sys.platform == "win32":
        base_qmake = "qmake.exe"
    else:
        base_qmake = "qmake"

    global opt_qmake

    for d in path.split(os.pathsep):
        opt_qmake = os.path.join(os.path.abspath(d), base_qmake)

        if os.access(opt_qmake, os.X_OK):
            break
    else:
        opt_qmake = None

    # Parse the command line.
    try:
        optlist, args = getopt.getopt(argv[1:], "hb:cd:g:ij:kl:m:q:ruv:w")
    except getopt.GetoptError:
        usage()

    global opt_pyqtbindir, opt_pyqtmoddir, opt_pyqtsipdir, opt_static
    global opt_debug, opt_concat, opt_split, opt_tracing, opt_verbose
    global opt_vendorcheck, opt_vendincdir, opt_vendlibdir

    for opt, arg in optlist:
        if opt == "-h":
            usage(0)
        elif opt == "-b":
            opt_pyqtbindir = os.path.abspath(arg)
        elif opt == "-c":
            opt_concat = 1
        elif opt == "-d":
            opt_pyqtmoddir = os.path.abspath(arg)
        elif opt == "-i":
            opt_vendorcheck = 1
        elif opt == "-j":
            try:
                opt_split = int(arg)
            except:
                usage()
        elif opt == "-k":
            opt_static = 1
        elif opt == "-l":
            opt_vendincdir = arg
        elif opt == "-m":
            opt_vendlibdir = arg
        elif opt == "-q":
            opt_qmake = os.path.abspath(arg)
        elif opt == "-r":
            opt_tracing = 1
        elif opt == "-u":
            opt_debug = 1
        elif opt == "-v":
            opt_pyqtsipdir = os.path.abspath(arg)
        elif opt == "-w":
            opt_verbose = 1

    # When building static libraries, signed interpreter checking makes no
    # sense.
    if opt_vendorcheck and opt_static:
        sipconfig.error("Using the VendorID package when building static libraries makes no sense.")

    # Get the details of the Qt installation.
    get_qt_configuration()

    # Replace the existing build macros with the ones from the Qt installation.
    macros = get_build_macros(args)

    if macros is None:
        usage()

    sipcfg.set_build_macros(macros)

    # Check Qt is what we need.
    pyqt = check_qt_installation(macros)

    # Check the licenses are compatible.
    check_license()

    # Check which modules to build.
    pyqt.check_modules()

    # Check for the VendorID package.
    check_vendorid()

    # Set the SIP platform, version and feature flags.
    set_sip_flags(pyqt)

    # Tell the user what's been found.
    inform_user()

    # Generate the code.
    pyqt.code()

    # Create the additional Makefiles.
    sipconfig.inform("Creating top level Makefile...")

    sipconfig.ParentMakefile(
        configuration=sipcfg,
        subdirs=pyqt_modules + pyqt.tools(),
        installs=(pyqt.module_installs(), opt_pyqtmoddir)
    ).generate()

    # Install the configuration module.
    create_config("pyqtconfig.py", "pyqtconfig.py.in", macros)


###############################################################################
# The script starts here.
###############################################################################

if __name__ == "__main__":
    try:
        main(sys.argv)
    except SystemExit:
        raise
    except:
        print \
"""An internal error occured.  Please report all the output from the program,
including the following traceback, to support@riverbankcomputing.co.uk.
"""
        raise
