# Copyright (c) 2007
# Riverbank Computing Limited <info@riverbankcomputing.co.uk>
#
# This file is part of PyTQt.
#
# This copy of PyTQt 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.
#
# PyTQt 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
# PyTQt; see the file LICENSE. If not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# This is the build script for PyTQt. It should be run in the top level
# directory of the source distribution and by the Python interpreter for which
# it is being built. It uses either qmake or tmake to do the hard work of
# generating the platform specific Makefiles.
import sys
import os
import glob
import tempfile
import shutil
import py_compile
import compileall
import string
# Get the SIP configuration.
try :
import sipconfig
except :
print " Unable to import the sipconfig module. Please make sure you have "
print " SIP v3.9 or later installed. "
sys . exit ( 1 )
config = sipconfig . SIPConfig ( " PyTQt 3.18.1 " )
# Initialise the globals.
sipMajorVersion = config . sip_version >> 16
sciIncDir = config . qt_inc_dir
sciLibDir = config . qt_lib_dir
sciLib = None
sciVersion = None
binDir = config . default_bin_dir
modDir = config . default_mod_dir
sipDir = config . default_sip_dir
buildModules = [ " qt " ]
tempBuildDir = None
catCppFiles = 0
catSplit = 1
qpeTag = None
trace = 0
releaseGIL = 0
def usage ( rcode = 2 ) :
""" Display a usage message and exit.
rcode is the return code passed back to the calling process .
"""
print " Usage: "
print " %s [-h] [-a version] [-b dir] [-c] [-d dir] [-g] [-j #] [-n dir] [-o dir] [-r] [-v dir] " % sipconfig . script ( )
print " where: "
print " -h display this help message "
print " -a tag explicitly enable the qtpe module "
print " -b dir where pyuic and pylupdate will be installed [default %s ] " % config . default_bin_dir
print " -c concatenate each module ' s C++ source files "
print " -d dir where the PyTQt modules will be installed [default %s ] " % config . default_mod_dir
print " -g always release the GIL (SIP v3.x behaviour) "
print " -j # split the concatenated C++ source files into # pieces [default 1] "
print " -n dir the directory containing the TQScintilla header files [default %s ] " % config . qt_inc_dir
print " -o dir the directory containing the TQScintilla library [default %s ] " % config . qt_lib_dir
print " -r generate code with tracing enabled [default disabled] "
print " -v dir where the PyTQt .sip files will be installed [default %s ] " % config . default_sip_dir
sys . exit ( rcode )
def mkTempBuildDir ( olddir = None ) :
""" Create a temporary build directory for a console application called
qttest , complete with patched Makefile . The global tempBuildDir is set to
the name of the directory . The temporary directory becomes the current
directory .
olddir is None if the directory should be created , otherwise it is deleted .
Returns the name of the previous current directory .
"""
global tempBuildDir
if olddir is None :
tempBuildDir = tempfile . mktemp ( )
try :
os . mkdir ( tempBuildDir )
except :
sipconfig . error ( " Unable to create temporary directory. " )
prevdir = sipconfig . push_dir ( tempBuildDir )
sipconfig . copy_to_file ( " qttest.pro.in " ,
""" TEMPLATE = app
TARGET = qttest
CONFIG + = console warn_off @TEST_OPENGL @ @BLX_CONFIG_APP @
INCLUDEPATH = @BLX_INCLUDEPATH @ @TEST_QSCINTILLA_INC @
DEFINES = @BLX_DEFINES @
SOURCES = qttest . cpp
LIBS + = @TEST_QUI_LIB @ @TEST_QSCINTILLA_LIB @
""" )
# Disable OpenGL, tqui and TQScintilla support by default.
config . patches [ " @TEST_OPENGL@ " ] = " "
config . patches [ " @TEST_QUI_LIB@ " ] = " "
config . patches [ " @TEST_QSCINTILLA_INC@ " ] = " "
config . patches [ " @TEST_QSCINTILLA_LIB@ " ] = " "
# Create a dummy source file to suppress a qmake warning.
sipconfig . copy_to_file ( " qttest.cpp " , " " )
config . create_makefile ( " qttest.pro " )
else :
sipconfig . pop_dir ( olddir )
prevdir = None
shutil . rmtree ( tempBuildDir , 1 )
return prevdir
def tryModule ( maindir , mname , incfile , ctor ) :
""" See if a PyTQt module should be built and update the buildModules list
accordingly .
maindir is the directory containing this script .
mname is the name of the PyTQt module .
incfile is the C + + header file that defines the class being used for the
test .
ctor is the constructor of the class being used for the test .
"""
# Check for the existence of the module .sip file.
msip = os . path . join ( maindir , " sip " , mname , mname + " mod.sip " )
if not os . access ( msip , os . F_OK ) :
return
sipconfig . copy_to_file ( " qttest.cpp " ,
""" #include < %s >
int main ( int argc , char * * argv )
{
new % s ;
}
""" % (incfile, ctor))
if sipconfig . run_make ( None , 0 ) == 0 :
buildModules . append ( mname )
sipconfig . inform ( " The %s module will be built. " % mname )
else :
sipconfig . inform ( " The %s module will not be built. " % mname )
sipconfig . run_make ( " clean " )
def checkTQScintilla ( ) :
""" See if TQScintilla can be found and what its version is.
"""
# Find the TQScintilla header files.
sciglobal = os . path . join ( sciIncDir , " qextscintillaglobal.h " )
if os . access ( sciglobal , os . F_OK ) :
config . patches [ " @PYQT_QSCINTILLA_INC@ " ] = sciIncDir
sipconfig . inform ( " %s contains qextscintillaglobal.h. " % ( sciIncDir ) )
# Get the TQScintilla version number.
global sciVersion
sciVersion , sciversstr = sipconfig . read_version ( sciglobal , " TQScintilla " , " TQSCINTILLA_VERSION " , " TQSCINTILLA_VERSION_STR " )
sipconfig . inform ( " TQScintilla %s is being used. " % ( sciversstr ) )
# If we find a snapshot then set the version number to 0 as a special
# case.
if sciversstr [ : 8 ] == " snapshot " :
sciVersion = 0
# Find the TQScintilla library.
if sys . platform == " win32 " :
lpatt = " qscintilla.lib "
else :
lpatt = " libqscintilla.* "
if len ( glob . glob ( os . path . join ( sciLibDir , lpatt ) ) ) :
sipconfig . inform ( " %s contains the TQScintilla library. " % sciLibDir )
global sciLib
if sys . platform == " win32 " :
sciLib = sipconfig . escape ( os . path . join ( sciLibDir , " qscintilla.lib " ) )
else :
sciLib = sipconfig . escape ( " -L " + sciLibDir ) + " -lqscintilla "
config . patches [ " @PYQT_QSCINTILLA_LIB@ " ] = sciLib
else :
sipconfig . inform ( " The TQScintilla library could not be found in %s and so the qtext module will not be built. If TQScintilla is installed then use the -o argument to explicitly specify the correct directory. " % ( sciLibDir ) )
sciVersion = - 1
else :
sipconfig . inform ( " qextscintillaglobal.h could not be found in %s and so the qtext module will not be built. If TQScintilla is installed then use the -n argument to explicitly specify the correct directory. " % sciIncDir )
sciVersion = - 1
def moduleChecks ( maindir ) :
""" See which PyTQt modules to build.
"""
sipconfig . inform ( " Checking which additional PyTQt modules to build. " ) ;
tryModule ( maindir , " qtcanvas " , " qcanvas.h " , " TQCanvas() " )
tryModule ( maindir , " qtnetwork " , " qsocket.h " , " TQSocket() " )
tryModule ( maindir , " qttable " , " qtable.h " , " TQTable() " )
tryModule ( maindir , " qtxml " , " qdom.h " , " TQDomImplementation() " )
if config . qt_version > = 0x030000 :
tryModule ( maindir , " qtsql " , " qsql.h " , " TQSql() " )
# We need a different Makefile for the qtgl module.
config . patches [ " @TEST_OPENGL@ " ] = " opengl "
config . create_makefile ( " qttest.pro " )
tryModule ( maindir , " qtgl " , " qgl.h " , " TQGLWidget() " )
# Put things back.
config . patches [ " @TEST_OPENGL@ " ] = " "
config . create_makefile ( " qttest.pro " )
# Check for the tqui library.
if config . qt_version > = 0x030000 :
if sys . platform == " win32 " :
tquilib = r " $(QTDIR) \ lib \ tqui.lib "
else :
tquilib = " -ltqui "
config . patches [ " @PYQT_QUI_LIB@ " ] = tquilib
config . patches [ " @TEST_QUI_LIB@ " ] = tquilib
config . create_makefile ( " qttest.pro " )
tryModule ( maindir , " qtui " , " qwidgetfactory.h " , " TQWidgetFactory() " )
# Put things back.
config . patches [ " @TEST_QUI_LIB@ " ] = " "
config . create_makefile ( " qttest.pro " )
# Check for the TQScintilla library.
if sciVersion > = 0 :
config . patches [ " @TEST_QSCINTILLA_INC@ " ] = sciIncDir
config . patches [ " @TEST_QSCINTILLA_LIB@ " ] = sciLib
config . create_makefile ( " qttest.pro " )
tryModule ( maindir , " qtext " , " qextscintillabase.h " , " QextScintillaBase() " )
# Put things back.
config . patches [ " @TEST_QSCINTILLA_INC@ " ] = " "
config . patches [ " @TEST_QSCINTILLA_LIB@ " ] = " "
config . create_makefile ( " qttest.pro " )
def generateFeatures ( featfile ) :
""" Generate the header file describing the TQt features that are enabled if
it doesn ' t already exist. (If it already exists then we are probably cross
compiling and generated the file through other means . )
featfile is the name of the features file .
"""
if os . access ( featfile , os . F_OK ) :
sipconfig . inform ( " Using existing features file. " )
return
sipconfig . inform ( " Generating the features file. " )
# The features that a given TQt configuration may or may not support. Note
# that STYLE_WINDOWSXP requires special handling.
flist = [ " ACTION " , " CLIPBOARD " , " CODECS " , " COLORDIALOG " , " DATASTREAM " ,
" DIAL " , " DNS " , " DOM " , " DRAGANDDROP " , " ICONVIEW " , " IMAGE_TEXT " ,
" INPUTDIALOG " , " FILEDIALOG " , " FONTDATABASE " , " FONTDIALOG " ,
" MESSAGEBOX " , " MIMECLIPBOARD " , " NETWORKPROTOCOL " , " PICTURE " ,
" PRINTDIALOG " , " PRINTER " , " PROGRESSDIALOG " , " PROPERTIES " ,
" SEMIMODAL " , " SIZEGRIP " , " SOUND " , " SPLITTER " , " STYLE_CDE " ,
" STYLE_INTERLACE " , " STYLE_MOTIF " , " STYLE_MOTIFPLUS " ,
" STYLE_PLATINUM " , " STYLE_SGI " , " STYLE_WINDOWS " , " TABDIALOG " ,
" TABLE " , " TABLEVIEW " , " TRANSFORMATIONS " , " TRANSLATION " , " WIZARD " ,
" WORKSPACE " ]
# Generate the program which will generate the features file.
f = open ( " qttest.cpp " , " w " )
# Escape the backslashes so that the name can be embedded in a C++ string.
ffstr = string . replace ( featfile , " \\ " , " \\ \\ " )
f . write (
""" #include <stdio.h>
#include <qglobal.h>
#include <qapplication.h>
int main ( int argc , char * * argv )
{
FILE * fp ;
TQApplication app ( argc , argv , 0 ) ;
if ( ( fp = fopen ( " %s " , " w " ) ) == NULL )
{
printf ( " Unable to create ' %s ' \\ n " ) ;
return 1 ;
}
#if !defined(QT_THREAD_SUPPORT)
fprintf ( fp , " -x TQt_THREAD_SUPPORT \\ n " ) ;
#endif
#if (defined(Q_OS_WIN32) || defined(Q_OS_WIN64)) && QT_VERSION >= 0x030000
if ( qWinVersion ( ) != TQt : : WV_XP )
fprintf ( fp , " -x TQt_STYLE_WINDOWSXP \\ n " ) ;
#endif
""" % (ffstr, ffstr))
for feat in flist :
f . write (
"""
#if defined(QT_NO_%s)
fprintf ( fp , " -x TQt_ %s \\ n " ) ;
#endif
""" % (feat, feat))
f . write (
"""
fclose ( fp ) ;
return 0 ;
}
""" )
f . close ( )
sipconfig . run_make ( )
sipconfig . run_program ( os . path . join ( os . getcwd ( ) , " qttest " ) )
sipconfig . run_make ( " clean " )
sipconfig . inform ( " Generated the features file. " )
def generateSource ( mname , plattag , qttag , xtrtag ) :
""" Generate the C++ source code for a particular PyTQt module.
mname is the name of the module .
plattag is the SIP tag for the platform .
qttag is the SIP tag for the TQt version .
xtrtag is an optional extra SIP tag .
"""
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 )
pro = mname + " .pro "
argv = [ " -t " , plattag ,
" -t " , qttag ,
" -z " , " features " ,
" -I " , " sip " ,
" -m " , mname + " / " + pro + " .in " ,
" -c " , mname ,
" sip/ " + mname + " / " + mname + " mod.sip " ]
if xtrtag :
argv . insert ( 0 , xtrtag )
argv . insert ( 0 , " -t " )
if trace :
argv . insert ( 0 , " -r " )
if releaseGIL :
argv . insert ( 0 , " -g " )
sipconfig . run_program ( config . sip_bin , argv )
# Generate the Makefile.
sipconfig . inform ( " Generating the Makefile for the %s module. " % mname )
olddir = sipconfig . push_dir ( mname )
if catCppFiles :
sipconfig . cat_source_files ( mname , catSplit )
config . create_makefile ( pro , mname )
icmds = [ ]
if sipMajorVersion == 3 :
icmds . append ( ( " copy " , mname + " .py " , modDir ) )
icmds . append ( ( " copy " , mname + " .pyc " , modDir ) )
config . add_install_target ( icmds )
if sipMajorVersion == 3 :
# Compile the Python part of the module.
pyname = mname + " .py "
sipconfig . inform ( " Compiling %s . " % ( pyname ) )
py_compile . compile ( pyname )
sipconfig . pop_dir ( olddir )
def versionToTag ( vers , tags , desc ) :
""" Convert a version number to a tag.
vers is the version number .
tags is the dictionary of tags keyed by version number .
desc is the descriptive name of the package .
Returns the corresponding tag .
"""
tag = None
vl = tags . keys ( )
vl . sort ( )
# For a snapshot use the latest tag.
if vers == 0 :
tag = tags [ vl [ - 1 ] ]
else :
for v in vl :
if vers < v :
tag = tags [ v ]
break
if tag is None :
sipconfig . error ( " Invalid %s version: 0x %06x . " % ( desc , vers ) )
return tag
def main ( argv ) :
""" The main function of the script.
argv is the list of command line arguments .
"""
import getopt
# Parse the command line.
try :
optlist , args = getopt . getopt ( argv [ 1 : ] , " ha:b:cd:gj:n:o:rv: " )
except getopt . GetoptError :
usage ( )
for opt , arg in optlist :
if opt == " -h " :
usage ( 0 )
elif opt == " -a " :
global qpeTag
qpeTag = arg
elif opt == " -b " :
global binDir
binDir = arg
elif opt == " -c " :
global catCppFiles
catCppFiles = 1
elif opt == " -d " :
global modDir
modDir = arg
elif opt == " -g " :
global releaseGIL
releaseGIL = 1
elif opt == " -j " :
global catSplit
try :
catSplit = int ( arg )
except :
catSplit = 0
if catSplit < 1 :
usage ( )
elif opt == " -n " :
global sciIncDir
sciIncDir = arg
elif opt == " -o " :
global sciLibDir
sciLibDir = arg
elif opt == " -r " :
global trace
trace = 1
elif opt == " -v " :
global sipDir
sipDir = arg
# Confirm the license.
sipconfig . confirm_license ( )
# If there should be a license file then check it is where it should be.
if config . license_file :
if os . access ( os . path . join ( " sip " , config . license_file ) , os . F_OK ) :
sipconfig . inform ( " Found the license file %s . \n " % config . license_file )
else :
sipconfig . error ( " Please copy the license file %s to the sip directory. \n " % config . license_file )
# Check the TQt version.
if config . qt_version == 0 :
sipconfig . error ( " SIP has been built with TQt support disabled. \n " )
# Early versions of TQt for the Mac didn't include everything. Rather than
# maintain these in the future we just mandate a later version.
if sys . platform == " darwin " and config . qt_version < 0x030100 :
sipconfig . error ( " PyTQt for MacOS/X requires TQt v3.1.0 or later. \n " )
# Check the installation directory is valid and add it as a patch.
if not os . access ( modDir , os . F_OK ) :
sipconfig . error ( " The %s PyTQt destination directory does not seem to exist. Use the -d argument to set the correct directory. " % ( modDir ) )
config . patches [ " @PYQT_MODDIR@ " ] = sipconfig . escape ( modDir )
sipconfig . inform ( " %s is the PyTQt installation directory. " % ( modDir ) )
# Enable warnings for SIP v4 generated code.
if sipMajorVersion > = 4 :
warn = " warn_on "
else :
warn = " warn_off "
config . patches [ " @PYQT_WARN@ " ] = warn
# Create patches to allow some modules to link against others.
if sipMajorVersion > = 4 :
modlink = " "
elif sys . platform == " win32 " :
modlink = sipconfig . escape ( os . path . join ( modDir , " libqtc.lib " ) )
else :
modlink = sipconfig . escape ( " -L " + modDir ) + " -lqtcmodule "
config . patches [ " @PYQT_QT_MODULE@ " ] = modlink
if sipMajorVersion > = 4 :
modlink = " "
elif sys . platform == " win32 " :
modlink = sipconfig . escape ( os . path . join ( modDir , " libqttablec.lib " ) ) + " " + sipconfig . escape ( os . path . join ( modDir , " libqtc.lib " ) )
else :
modlink = sipconfig . escape ( " -L " + modDir ) + " -lqttablecmodule -lqtcmodule "
config . patches [ " @PYQT_QTTABLE_MODULE@ " ] = modlink
# The professional edition needs special handling if XML support is needed.
if config . qt_edition == " professional " :
rbprof = " rbprof "
else :
rbprof = " "
config . patches [ " @PYQT_RBPROF@ " ] = rbprof
# Link in the qassistantclient library for TQt v3.1+.
qaclib = " "
if config . qt_version > = 0x030100 :
if sys . platform == " win32 " :
qaclib = r " $(QTDIR) \ lib \ qassistantclient.lib "
else :
qaclib = " -lqassistantclient "
config . patches [ " @PYQT_QASSISTANTCLIENT_LIB@ " ] = qaclib
# Check for TQScintilla.
if config . qt_version > = 0x030000 :
checkTQScintilla ( )
# Create a build directory that we can compile test programs.
maindir = mkTempBuildDir ( )
# Check what additional modules to build.
if config . qt_version > = 0x020000 :
moduleChecks ( maindir )
# Work out the platform and TQt version tags to pass to SIP to generate the
# code we need.
if config . qt_lib == " qte " :
plattag = " WS_QWS "
elif sys . platform == " win32 " :
plattag = " WS_WIN "
elif sys . platform == " darwin " :
plattag = " WS_MACX "
else :
plattag = " WS_X11 "
qttags = {
0x020000 : " TQt_1_43 " ,
0x020100 : " TQt_2_00 " ,
0x020200 : " TQt_2_1_0 " ,
0x020300 : " TQt_2_2_0 " ,
0x020301 : " TQt_2_3_0 " ,
0x030000 : " TQt_2_3_1 " ,
0x030001 : " TQt_3_0_0 " ,
0x030002 : " TQt_3_0_1 " ,
0x030004 : " TQt_3_0_2 " ,
0x030005 : " TQt_3_0_4 " ,
0x030006 : " TQt_3_0_5 " ,
0x030100 : " TQt_3_0_6 " ,
0x030101 : " TQt_3_1_0 " ,
0x030102 : " TQt_3_1_1 " ,
0x030200 : " TQt_3_1_2 " ,
0x030300 : " TQt_3_2_0 " ,
0x040000 : " TQt_3_3_0 "
}
qttag = versionToTag ( config . qt_version , qttags , " TQt " )
# Work out the TQScintilla tag.
if sciVersion > = 0 :
scitags = {
0x010100 : " TQScintilla_1_0 " ,
0x010200 : " TQScintilla_1_1 " ,
0x020000 : " TQScintilla_1_2 "
}
scitag = versionToTag ( sciVersion , scitags , " TQScintilla " )
else :
scitag = None
# Generate the features file.
generateFeatures ( os . path . join ( maindir , " features " ) )
# We don't need the temporary build directory anymore.
mkTempBuildDir ( maindir )
subdirs = [ ]
for mname in buildModules :
if mname == " qtext " :
xtratag = scitag
else :
xtratag = None
generateSource ( mname , plattag , qttag , xtratag )
subdirs . append ( mname )
# We handle the qtpe module explicitly rather than auto-detect. This is
# because it does things a bit differently and I'm too lazy to deal with it
# properly at the moment.
if qpeTag :
generateSource ( " qtpe " , plattag , qttag , qpeTag )
subdirs . append ( " qtpe " )
# Install the .sip files.
sipconfig . inform ( " Creating Makefile for .sip files. " )
olddir = sipconfig . push_dir ( " sip " )
sipconfig . copy_to_file ( " Makefile " , " all: \n " )
icmds = [ ]
for mname in buildModules :
dstdir = os . path . join ( sipDir , mname )
icmds . append ( ( " mkdir " , dstdir , None ) )
for sf in os . listdir ( os . path . join ( olddir , " sip " , mname ) ) :
icmds . append ( ( " copy " , os . path . join ( mname , sf ) , os . path . join ( dstdir , sf ) ) )
config . add_install_target ( icmds )
sipconfig . pop_dir ( olddir )
subdirs . append ( " sip " )
# See which version of pyuic to build.
config . patches [ " @PYQT_BINDIR@ " ] = sipconfig . escape ( binDir )
if config . qt_version > = 0x030000 :
sipconfig . inform ( " Creating Makefile for pyuic3. " )
subdirs . append ( " pyuic3 " )
olddir = sipconfig . push_dir ( " pyuic3 " )
elif config . qt_version > = 0x020000 :
sipconfig . inform ( " Creating Makefile for pyuic2. " )
subdirs . append ( " pyuic2 " )
olddir = sipconfig . push_dir ( " pyuic2 " )
config . create_makefile ( " pyuic.pro " , [ ] )
sipconfig . pop_dir ( olddir )
# Build pylupdate if TQt v3.0 or later.
if config . qt_version > = 0x030000 :
sipconfig . inform ( " Creating Makefile for pylupdate3. " )
subdirs . append ( " pylupdate3 " )
olddir = sipconfig . push_dir ( " pylupdate3 " )
config . create_makefile ( " pylupdate.pro " , [ ] )
sipconfig . pop_dir ( olddir )
# Generate the top-level Makefile.
sipconfig . inform ( " Creating top level Makefile. " )
sipconfig . copy_to_file ( " PyTQt.pro.in " , " TEMPLATE = subdirs \n SUBDIRS = " + string . join ( subdirs ) + " \n " )
config . create_makefile ( " PyTQt.pro " )
# Tell the user what to do next.
msg = " The build of the PyTQt source code for your system is now complete. To compile and install PyTQt run \" %s \" and \" %s install \" with appropriate user privileges. " % ( config . make_bin , config . make_bin )
sipconfig . inform ( msg )
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