# Copyright 2002-2006 Vladimir Prus # Copyright 2005 Alo Sarv # Copyright 2005-2009 Juergen Hunold # # Distributed under the Boost Software License, Version 1.0. (See # accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) # Qt4 library support module # # The module attempts to auto-detect QT installation location from QTDIR # environment variable; failing that, installation location can be passed as # argument: # # toolset.using qt4 : /usr/local/Trolltech/Qt-4.0.0 ; # # The module supports code generation from .ui and .qrc files, as well as # running the moc preprocessor on headers. Note that you must list all your # moc-able headers in sources. # # Example: # # exe myapp : myapp.cpp myapp.h myapp.ui myapp.qrc # /qt4//QtGui /qt4//QtNetwork ; # # It's also possible to run moc on cpp sources: # # import cast ; # # exe myapp : myapp.cpp [ cast _ moccable-cpp : myapp.cpp ] /qt4//QtGui ; # # When moccing source file myapp.cpp you need to include "myapp.moc" from # myapp.cpp. When moccing .h files, the output of moc will be automatically # compiled and linked in, you don't need any includes. # # This is consistent with Qt guidelines: # http://doc.trolltech.com/4.0/moc.html import modules ; import feature ; import errors ; import type ; import "class" : new ; import generators ; import project ; import toolset : flags ; import os ; import virtual-target ; import scanner ; # Qt3Support control feature # # Qt4 configure defaults to build Qt4 libraries with Qt3Support. # The autodetection is missing, so we default to disable Qt3Support. # This prevents the user from inadvertedly using a deprecated API. # # The Qt3Support library can be activated by adding # "on" to requirements # # Use "on:QT3_SUPPORT_WARNINGS" # to get warnings about deprecated Qt3 support funtions and classes. # Files ported by the "qt3to4" conversion tool contain _tons_ of # warnings, so this define is not set as default. # # Todo: Detect Qt3Support from Qt's configure data. # Or add more auto-configuration (like python). feature.feature qt3support : off on : propagated link-incompatible ; # The Qt version used for requirements # Valid are 4.4 or 4.5.0 # Auto-detection via qmake sets 'major.minor.patch' feature.feature qt : : propagated ; project.initialize $(__name__) ; project qt ; # Save the project so that we tolerate 'import + using' combo. .project = [ project.current ] ; # Helper utils for easy debug output if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ] { .debug-configuration = TRUE ; } local rule debug-message ( message * ) { if $(.debug-configuration) = TRUE { ECHO notice: [qt4-cfg] $(message) ; } } # Capture qmake output line by line local rule read-output ( content ) { local lines ; local nl = " " ; local << = "([^$(nl)]*)[$(nl)](.*)" ; local line+ = [ MATCH "$(<<)" : "$(content)" ] ; while $(line+) { lines += $(line+[1]) ; line+ = [ MATCH "$(<<)" : "$(line+[2])" ] ; } return $(lines) ; } # Capture Qt version from qmake local rule check-version ( bin_prefix ) { full-cmd = $(bin_prefix)"/qmake -v" ; debug-message Running '$(full-cmd)' ; local output = [ SHELL $(full-cmd) ] ; for line in [ read-output $(output) ] { # Parse the output to get all the results. if [ MATCH "QMake" : $(line) ] { # Skip first line of output } else { temp = [ MATCH "([0-9]*)\\.([0-9]*)\\.([0-9]*)" : $(line) ] ; } } return $(temp) ; } # Validate the version string and extract the major/minor part we care about. # local rule split-version ( version ) { local major-minor = [ MATCH ^([0-9]+)\.([0-9]+)(.*)$ : $(version) : 1 2 3 ] ; if ! $(major-minor[2]) || $(major-minor[3]) { ECHO "Warning: 'using qt' expects a two part (major, minor) version number; got" $(version) instead ; # Add a zero to account for the missing digit if necessary. major-minor += 0 ; } return $(major-minor[1]) $(major-minor[2]) ; } # Initialize the QT support module. # Parameters: # - 'prefix' parameter tells where Qt is installed. # - 'full_bin' optional full path to Qt binaries (qmake,moc,uic,rcc) # - 'full_inc' optional full path to Qt top-level include directory # - 'full_lib' optional full path to Qt library directory # - 'version' optional version of Qt, else autodetected via 'qmake -v' # - 'condition' optional requirements rule init ( prefix : full_bin ? : full_inc ? : full_lib ? : version ? : condition * ) { project.push-current $(.project) ; debug-message "==== Configuring Qt ... ====" ; for local v in version cmd-or-prefix includes libraries condition { if $($(v)) { debug-message " user-specified "$(v): '$($(v))' ; } } # Needed as default value .prefix = $(prefix) ; # pre-build paths to detect reinitializations changes local inc_prefix lib_prefix bin_prefix ; if $(full_inc) { inc_prefix = $(full_inc) ; } else { inc_prefix = $(prefix)/include ; } if $(full_lib) { lib_prefix = $(full_lib) ; } else { lib_prefix = $(prefix)/lib ; } if $(full_bin) { bin_prefix = $(full_bin) ; } else { bin_prefix = $(prefix)/bin ; } # Globally needed variables .incprefix = $(inc_prefix) ; .libprefix = $(lib_prefix) ; .binprefix = $(bin_prefix) ; if ! $(.initialized) { # Make sure this is initialised only once .initialized = true ; # Generates cpp files from header files using "moc" tool generators.register-standard qt4.moc : H : CPP(moc_%) : qt4 ; # The OBJ result type is a fake, 'H' will be really produced. See # comments on the generator class, defined below the 'init' function. generators.register [ new uic-generator qt4.uic : UI : OBJ : qt4 ] ; # The OBJ result type is a fake here too. generators.register [ new moc-h-generator qt4.moc.inc : MOCCABLE_CPP : OBJ : qt4 ] ; generators.register [ new moc-inc-generator qt4.moc.inc : MOCCABLE_H : OBJ : qt4 ] ; # Generates .cpp files from .qrc files. generators.register-standard qt4.rcc : QRC : CPP(qrc_%) ; # dependency scanner for wrapped files. type.set-scanner QRC : qrc-scanner ; # Save value of first occuring prefix .PREFIX = $(prefix) ; } if $(version) { major-minor = [ split-version $(version) ] ; version = $(major-minor:J=.) ; } else { version = [ check-version $(bin_prefix) ] ; if $(version) { version = $(version:J=.) ; } debug-message Detected version '$(version)' ; } local target-requirements = $(condition) ; # Add the version, if any, to the target requirements. if $(version) { if ! $(version) in [ feature.values qt ] { feature.extend qt : $(version) ; } target-requirements += $(version:E=default) ; } local target-os = [ feature.get-values target-os : $(condition) ] ; if ! $(target-os) { target-os ?= [ feature.defaults target-os ] ; target-os = $(target-os:G=) ; target-requirements += $(target-os) ; } # Build exact requirements for the tools local tools-requirements = $(target-requirements:J=/) ; debug-message "Details of this Qt configuration:" ; debug-message " prefix: " '$(prefix:E=)' ; debug-message " binary path: " '$(bin_prefix:E=)' ; debug-message " include path:" '$(inc_prefix:E=)' ; debug-message " library path:" '$(lib_prefix:E=)' ; debug-message " target requirements:" '$(target-requirements)' ; debug-message " tool requirements: " '$(tools-requirements)' ; # setup the paths for the tools toolset.flags qt4.moc .BINPREFIX $(tools-requirements) : $(bin_prefix) ; toolset.flags qt4.rcc .BINPREFIX $(tools-requirements) : $(bin_prefix) ; toolset.flags qt4.uic .BINPREFIX $(tools-requirements) : $(bin_prefix) ; # TODO: 2009-02-12: Better support for directories # Most likely needed are separate getters for: include,libraries,binaries and sources. toolset.flags qt4.directory .PREFIX $(tools-requirements) : $(prefix) ; # Test for a buildable Qt. if [ glob $(.prefix)/Jamroot ] { .bjam-qt = true # this will declare QtCore (and qtmain on windows) add-shared-library QtCore ; } else # Setup common pre-built Qt. # Special setup for QtCore on which everything depends { local usage-requirements = $(.incprefix) $(.libprefix) $(.libprefix) multi qt4 ; local suffix ; # Since Qt-4.2, debug versions on unix have to be built # separately and therefore have no suffix. .suffix_version = "" ; .suffix_debug = "" ; # Control flag for auto-configuration of the debug libraries. # This setup requires Qt 'configure -debug-and-release'. # Only available on some platforms. # ToDo: 2009-02-12: Maybe throw this away and # require separate setup with debug as condition. .have_separate_debug = FALSE ; # Setup other platforms if $(target-os) in windows cygwin { .have_separate_debug = TRUE ; # On NT, the libs have "4" suffix, and "d" suffix in debug builds. .suffix_version = "4" ; .suffix_debug = "d" ; # On Windows we must link against the qtmain library lib qtmain : # sources : # requirements qtmain$(.suffix_debug) debug $(target-requirements) ; lib qtmain : # sources : # requirements qtmain $(target-requirements) ; } else if $(target-os) = darwin { # On MacOS X, both debug and release libraries are available. .suffix_debug = "_debug" ; .have_separate_debug = TRUE ; alias qtmain ; } else { alias qtmain : : $(target-requirements) ; } lib QtCore : qtmain : # requirements QtCore$(.suffix_version) $(target-requirements) : # default-build : # usage-requirements QT_CORE_LIB QT_NO_DEBUG $(.incprefix)/QtCore $(usage-requirements) ; if $(.have_separate_debug) = TRUE { debug-message Configure debug libraries with suffix '$(.suffix_debug)' ; lib QtCore : $(main) : # requirements QtCore$(.suffix_debug)$(.suffix_version) debug $(target-requirements) : # default-build : # usage-requirements QT_CORE_LIB $(.incprefix)/QtCore $(usage-requirements) ; } } # Initialising the remaining libraries is canonical # parameters 'module' : 'depends-on' : 'usage-define' : 'requirements' : 'include' # 'include' only for non-canonical include paths. add-shared-library QtGui : QtCore : QT_GUI_LIB : $(target-requirements) ; add-shared-library QtNetwork : QtCore : QT_NETWORK_LIB : $(target-requirements) ; add-shared-library QtSql : QtCore : QT_SQL_LIB : $(target-requirements) ; add-shared-library QtXml : QtCore : QT_XML_LIB : $(target-requirements) ; add-shared-library Qt3Support : QtGui QtNetwork QtXml QtSql : QT_QT3SUPPORT_LIB QT3_SUPPORT : on $(target-requirements) ; # Dummy target to enable "off" and # "/qt//Qt3Support" at the same time. This enables quick # switching from one to the other for test/porting purposes. alias Qt3Support : : off $(target-requirements) ; # OpenGl Support add-shared-library QtOpenGL : QtGui : QT_OPENGL_LIB : $(target-requirements) ; # SVG-Support (Qt 4.1) add-shared-library QtSvg : QtXml QtOpenGL : QT_SVG_LIB : $(target-requirements) ; # Test-Support (Qt 4.1) add-shared-library QtTest : QtCore : : $(target-requirements) ; # Qt designer library add-shared-library QtDesigner : QtGui QtXml : : $(target-requirements) ; add-shared-library QtDesignerComponents : QtGui QtXml : : $(target-requirements) ; # Support for dynamic Widgets (Qt 4.1) add-static-library QtUiTools : QtGui QtXml : $(target-requirements) ; # DBus-Support (Qt 4.2) add-shared-library QtDBus : QtXml : : $(target-requirements) ; # Script-Engine (Qt 4.3) add-shared-library QtScript : QtGui QtXml : QT_SCRIPT_LIB : $(target-requirements) ; # Tools for the Script-Engine (Qt 4.5) add-shared-library QtScriptTools : QtScript : QT_SCRIPTTOOLS_LIB : $(target-requirements) ; # WebKit (Qt 4.4) add-shared-library QtWebKit : QtGui : QT_WEBKIT_LIB : $(target-requirements) ; # Phonon Multimedia (Qt 4.4) add-shared-library phonon : QtGui QtXml : QT_PHONON_LIB : $(target-requirements) ; # Multimedia engine (Qt 4.6) add-shared-library QtMultimedia : QtGui : QT_MULTIMEDIA_LIB : $(target-requirements) ; # XmlPatterns-Engine (Qt 4.4) add-shared-library QtXmlPatterns : QtNetwork : QT_XMLPATTERNS_LIB : $(target-requirements) ; # Help-Engine (Qt 4.4) add-shared-library QtHelp : QtGui QtSql QtXml : : $(target-requirements) ; add-shared-library QtCLucene : QCore QtSql QtXml : : $(target-requirements) ; # QML-Engine (Qt 4.7) add-shared-library QtDeclarative : QtGui QtXml : : $(target-requirements) ; # AssistantClient Support # Compat library removed in 4.7.0 # Pre-4.4 help system, use QtHelp for new programs if $(version) < "4.7" { add-shared-library QtAssistantClient : QtGui : : $(target-requirements) : QtAssistant ; } debug-message "==== Configured Qt-$(version) ====" ; project.pop-current ; } rule initialized ( ) { return $(.initialized) ; } # This custom generator is needed because in QT4, UI files are translated only # into H files, and no C++ files are created. Further, the H files need not be # passed via MOC. The header is used only via inclusion. If we define a standard # UI -> H generator, Boost.Build will run MOC on H, and then compile the # resulting cpp. It will give a warning, since output from moc will be empty. # # This generator is declared with a UI -> OBJ signature, so it gets invoked when # linking generator tries to convert sources to OBJ, but it produces target of # type H. This is non-standard, but allowed. That header won't be mocced. # class uic-generator : generator { rule __init__ ( * : * ) { generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; } rule run ( project name ? : property-set : sources * ) { if ! $(name) { name = [ $(sources[0]).name ] ; name = $(name:B) ; } local a = [ new action $(sources[1]) : qt4.uic : $(property-set) ] ; # The 'ui_' prefix is to match qmake's default behavior. local target = [ new file-target ui_$(name) : H : $(project) : $(a) ] ; local r = [ virtual-target.register $(target) ] ; # Since this generator will return a H target, the linking generator # won't use it at all, and won't set any dependency on it. However, we # need the target to be seen by bjam, so that dependency from sources to # this generated header is detected -- if jam does not know about this # target, it won't do anything. DEPENDS all : [ $(r).actualize ] ; return $(r) ; } } class moc-h-generator : generator { rule __init__ ( * : * ) { generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; } rule run ( project name ? : property-set : sources * ) { if ! $(sources[2]) && [ $(sources[1]).type ] = MOCCABLE_CPP { name = [ $(sources[0]).name ] ; name = $(name:B) ; local a = [ new action $(sources[1]) : qt4.moc.inc : $(property-set) ] ; local target = [ new file-target $(name) : MOC : $(project) : $(a) ] ; local r = [ virtual-target.register $(target) ] ; # Since this generator will return a H target, the linking generator # won't use it at all, and won't set any dependency on it. However, # we need the target to be seen by bjam, so that dependency from # sources to this generated header is detected -- if jam does not # know about this target, it won't do anything. DEPENDS all : [ $(r).actualize ] ; return $(r) ; } } } class moc-inc-generator : generator { rule __init__ ( * : * ) { generator.__init__ $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : $(9) ; } rule run ( project name ? : property-set : sources * ) { if ! $(sources[2]) && [ $(sources[1]).type ] = MOCCABLE_H { name = [ $(sources[0]).name ] ; name = $(name:B) ; local a = [ new action $(sources[1]) : qt4.moc.inc : $(property-set) ] ; local target = [ new file-target moc_$(name) : CPP : $(project) : $(a) ] ; # Since this generator will return a H target, the linking generator # won't use it at all, and won't set any dependency on it. However, # we need the target to be seen by bjam, so that dependency from # sources to this generated header is detected -- if jam does not # know about this target, it won't do anything. DEPENDS all : [ $(target).actualize ] ; return [ virtual-target.register $(target) ] ; } } } # Query the installation directory. This is needed in at least two scenarios. # First, when re-using sources from the Qt-Tree. Second, to "install" custom Qt # plugins to the Qt-Tree. # rule directory { return $(.PREFIX) ; } # Add a shared Qt library. rule add-shared-library ( lib-name : depends-on * : usage-defines * : requirements * : include ? ) { add-library $(lib-name) : $(.suffix_version) : $(depends-on) : $(usage-defines) : $(requirements) : $(include) ; } # Add a static Qt library. rule add-static-library ( lib-name : depends-on * : usage-defines * : requirements * : include ? ) { add-library $(lib-name) : : $(depends-on) : $(usage-defines) : $(requirements) : $(include) ; } # Add a Qt library. # Static libs are unversioned, whereas shared libs have the major number as suffix. # Creates both release and debug versions on platforms where both are enabled by Qt configure. # Flags: # - lib-name Qt library Name # - version Qt major number used as shared library suffix (QtCore4.so) # - depends-on other Qt libraries # - usage-defines those are set by qmake, so set them when using this library # - requirements addional requirements # - include non-canonical include path. The canonical path is $(.incprefix)/$(lib-name). rule add-library ( lib-name : version ? : depends-on * : usage-defines * : requirements * : include ? ) { if $(.bjam-qt) { # Import Qt module # Eveything will be setup there alias $(lib-name) : $(.prefix)//$(lib-name) : : : qt4 ; } else { local real_include ; real_include ?= $(include) ; real_include ?= $(lib-name) ; lib $(lib-name) : # sources $(depends-on) : # requirements $(lib-name)$(version) $(requirements) : # default-build : # usage-requirements $(usage-defines) $(.incprefix)/$(real_include) ; if $(.have_separate_debug) = TRUE { lib $(lib-name) : # sources $(depends-on) : # requirements $(lib-name)$(.suffix_debug)$(version) $(requirements) debug : # default-build : # usage-requirements $(usage-defines) $(.incprefix)/$(real_include) ; } } # Make library explicit so that a simple qt4 will not bring in everything. # And some components like QtDBus/Phonon may not be available on all platforms. explicit $(lib-name) ; } # Use $(.BINPREFIX[-1]) for the paths as several tools-requirements can match. # The exact match is the last one. # Get and from current toolset. flags qt4.moc INCLUDES ; flags qt4.moc DEFINES ; # need a newline for expansion of DEFINES and INCLUDES in the response file. .nl = " " ; # Processes headers to create Qt MetaObject information. Qt4-moc has its # c++-parser, so pass INCLUDES and DEFINES. # We use response file with one INCLUDE/DEFINE per line # actions moc { $(.BINPREFIX[-1])/moc -f $(>) -o $(<) @"@($(<).rsp:E=-D$(DEFINES)$(.nl) -I$(INCLUDES:T)$(.nl))" } # When moccing files for include only, we don't need -f, otherwise the generated # code will include the .cpp and we'll get duplicated symbols. # actions moc.inc { $(.BINPREFIX[-1])/moc $(>) -o $(<) @"@($(<).rsp:E=-D$(DEFINES)$(.nl) -I$(INCLUDES:T)$(.nl))" } # Generates source files from resource files. # actions rcc { $(.BINPREFIX[-1])/rcc $(>) -name $(>:B) -o $(<) } # Generates user-interface source from .ui files. # actions uic { $(.BINPREFIX[-1])/uic $(>) -o $(<) } # Scanner for .qrc files. Look for the CDATA section of the tag. Ignore # the "alias" attribute. See http://doc.trolltech.com/qt/resources.html for # detailed documentation of the Qt Resource System. # class qrc-scanner : common-scanner { rule pattern ( ) { return "(.*)" ; } } # Wrapped files are "included". scanner.register qrc-scanner : include ;