summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-07-24 08:11:18 (GMT)
committerSteven Knight <knight@baldmt.com>2003-07-24 08:11:18 (GMT)
commit7a3d5b8e06e44fee43fc78d7eb72b8d271022c18 (patch)
tree9644777ce4b886806bd13ce207e047da336a7b12
parent5d238601a22fa009de4df38e0257c405f9b243db (diff)
downloadSCons-7a3d5b8e06e44fee43fc78d7eb72b8d271022c18.zip
SCons-7a3d5b8e06e44fee43fc78d7eb72b8d271022c18.tar.gz
SCons-7a3d5b8e06e44fee43fc78d7eb72b8d271022c18.tar.bz2
Add a QT tool. (Christoph Wiedemann)
-rw-r--r--bin/files1
-rw-r--r--doc/man/scons.1103
-rw-r--r--etc/TestSCons.py19
-rw-r--r--src/CHANGES.txt4
-rw-r--r--src/engine/MANIFEST.in1
-rw-r--r--src/engine/SCons/Builder.py20
-rw-r--r--src/engine/SCons/BuilderTests.py29
-rw-r--r--src/engine/SCons/Defaults.py1
-rw-r--r--src/engine/SCons/Node/NodeTests.py38
-rw-r--r--src/engine/SCons/Node/__init__.py13
-rw-r--r--src/engine/SCons/Tool/qt.py214
-rw-r--r--test/QT.py377
-rw-r--r--test/QTFLAGS.py238
-rw-r--r--test/import.py1
14 files changed, 1049 insertions, 10 deletions
diff --git a/bin/files b/bin/files
index c4b19a3..baeeb14 100644
--- a/bin/files
+++ b/bin/files
@@ -70,6 +70,7 @@
./SCons/Tool/nasm.py
./SCons/Tool/pdflatex.py
./SCons/Tool/pdftex.py
+./SCons/Tool/qt.py
./SCons/Tool/rmic.py
./SCons/Tool/sgiar.py
./SCons/Tool/sgicc.py
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 49fe6fd..29b2bb2 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -895,6 +895,7 @@ msvs
nasm
pdflatex
pdftex
+qt
rmic
sgiar
sgicc
@@ -1351,7 +1352,8 @@ env.CFile(target = 'bar', source = 'bar.y')
.EE
.IP CXXFile
-Builds a C++ source file given a lex (.ll) or yacc (.yy) input file.
+Builds a C++ source file given a lex (.ll), yacc (.yy)
+or uic (.ui) input file.
The suffix specified by the $CXXFILESUFFIX construction variable
(.cc by default)
is automatically added to the target
@@ -3068,6 +3070,105 @@ The prefix used for PostScript file names.
.IP PSSUFFIX
The prefix used for PostScript file names.
+.IP QTDIR
+The qt tool tries to take this from os.environ.
+It also initializes all QT_*
+construction variables listed below.
+(Note that all paths are constructed
+with python's os.path.join() method, but listed here with the '/' seperator
+for easier reading.)
+In addition, the construction environment
+variables CPPPATH, LIBPATH, LIBS, PROGEMITTER, SHLIBEMITTER and LIBEMITTER
+are modified. Because the build-performance is affected when using this tool,
+you have to explicitly specify it at Environment creation:
+.ES
+Environment(tools=['default','qt']).
+.EE
+.IP
+You may want to use
+.B Configure
+to verify that the qt support really works.
+The qt tool supports the following operations:
+
+.B Automatic moc file generation from header files.
+You do not have to specify moc files explicitly, the tool does it for you.
+However, there are a few preconditions to do so: Your header file must have
+the same filebase as your implementation file and must stay in the same
+directory. It must have one of the suffixes .h, .hpp, .H, .hxx, .hh.
+
+.B Automatic moc file generation from cxx files.
+As stated in the qt documentation,
+include the moc file at the end of the cxx file.
+Note that you have to include the file, which is generated by the
+QT_MOCNAMEGENERATOR function. If you are using BuildDir, you may need to
+specify duplicate=1.
+
+.B Automatic handling of .ui files.
+The implementation files generated from .ui files are handled much the same
+as yacc or lex files. Because there are also generated headers, you may
+need to specify duplicate=1 in calls to BuildDir.
+
+.IP QT_LIB
+Default value is 'qt'. You may want to set this to 'qt-mt'
+
+.IP QT_MOC
+Default value is '$QTDIR/bin/moc'.
+
+.IP QT_UIC
+Default value is '$QTDIR/bin/uic'.
+
+.IP QT_UICIMPLFLAGS
+Default value is ''. These flags are passed to uic, when creating a cxx
+file from a .ui file.
+
+.IP QT_UICDECLFLAGS
+Default value is ''. These flags are passed to uic, when creating a a h
+file from a .ui file.
+
+.IP QT_MOCFROMHFLAGS
+Default value is ''. These flags are passed to moc, when moccing a header
+file.
+
+.IP QT_MOCFROMCPPFLAGS
+Default value is '-i'. These flags are passed to moc, when moccing a
+cpp file.
+
+.IP QT_HSUFFIX
+Default value is '.h'. Suffix of headers generated with uic.
+
+.IP QT_UISUFFIX
+Default value is '.ui'. Suffix of designer files.
+
+.IP QT_UIHSUFFIX
+Default value is '.ui.h'.
+
+.IP QT_MOCNAMEGENERATOR
+Three-argument function, which generates names of moc output files.
+This is the most flexible way to support the huge number of conventions
+for this type of files. The arguments are the
+.I filebase
+, which is the file to be moc'd without path and extension, the
+.I src_suffix
+, which is the extension of the file to be moc'd and the environment
+.I env
+The default value maps 'myfile.myext' to 'moc_myfile.$CXXFILESUFFIX':
+
+.ES
+lambda filebase, src_suffix, env: 'moc_' + filebase + env['CXXFILESUFFIX']
+.EE
+
+.IP QT_UICIMPLCOM
+Command to generate cxx files from .ui files.
+
+.IP QT_UICDECLCOM
+Command to generate header files from .ui files.
+
+.IP QT_MOCFROMHCOM
+Command to generate a moc file from a header.
+
+.IP QT_MOCFROMCXXCOM
+Command to generate a moc file from a cpp file.
+
.IP RANLIB
The archive indexer.
diff --git a/etc/TestSCons.py b/etc/TestSCons.py
index 786b394..59b0647 100644
--- a/etc/TestSCons.py
+++ b/etc/TestSCons.py
@@ -264,3 +264,22 @@ class TestSCons(TestCmd.TestCmd):
kw['arguments'] = arguments
kw['stdout'] = self.wrap_stdout(build_str = s)
apply(self.run, [], kw)
+
+ def not_up_to_date(self, options = None, arguments = None, **kw):
+ """Asserts that none of the targets listed in arguments is
+ up to date, but does not make any assumptions on other targets.
+ This function is most useful in conjunction with the -n option.
+ """
+ s = ""
+ for arg in string.split(arguments):
+ s = s + "(?!scons: `%s' is up to date.)" % arg
+ if options:
+ arguments = options + " " + arguments
+ kw['arguments'] = arguments
+ stdout = self.wrap_stdout(build_str="("+s+"[^\n]*\n)*")
+ stdout = string.replace(stdout,'\n','\\n')
+ stdout = string.replace(stdout,'.','\\.')
+ old_match_func = self.match_func
+ self.match_func = TestCmd.match_re_dotall
+ apply(self.run, [], kw)
+ self.match_func = old_match_func
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 4b8b330..1b21aa8 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -68,7 +68,7 @@ RELEASE 0.XX - XXX
- When the -debug=pdb option is specified, use pdb.Pdb().runcall() to
call pdb directly, don't call Python recursively.
- From Christoph Wiedemann
+ From Christoph Wiedemann:
- Have the g++ Tool actually use g++ in preference to c++.
@@ -81,6 +81,8 @@ RELEASE 0.XX - XXX
- Avoid SCons hanging when a piped command has a lot of output to read.
+ - Add QT support for preprocessing .ui files into .c files.
+
RELEASE 0.90 - Wed, 25 Jun 2003 14:24:52 -0500
diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in
index 53c8c8a..3cfcff8 100644
--- a/src/engine/MANIFEST.in
+++ b/src/engine/MANIFEST.in
@@ -84,6 +84,7 @@ SCons/Tool/pdflatex.py
SCons/Tool/pdftex.py
SCons/Tool/Perforce.py
SCons/Tool/PharLapCommon.py
+SCons/Tool/qt.py
SCons/Tool/RCS.py
SCons/Tool/rmic.py
SCons/Tool/SCCS.py
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 2d5828a..2bcd993 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -89,11 +89,25 @@ class DictCmdGenerator:
if not ext:
raise UserError("While building `%s': Cannot deduce file extension from source files: %s" % (repr(map(str, target)), repr(map(str, source))))
try:
- # XXX Do we need to perform Environment substitution
- # on the keys of action_dict before looking it up?
return self.action_dict[ext]
except KeyError:
- raise UserError("While building `%s': Don't know how to build a file with suffix %s." % (repr(map(str, target)), repr(ext)))
+ # Before raising the user error, try to perform Environment
+ # substitution on the keys of action_dict.
+ s_dict = {}
+ for (k,v) in self.action_dict.items():
+ s_k = env.subst(k)
+ if s_dict.has_key(s_k):
+ # XXX Note that we do only raise errors, when variables
+ # point to the same suffix. If one suffix is a
+ # literal and a variable suffix contains this literal
+ # we don't raise an error (cause the literal 'wins')
+ raise UserError("Ambiguous suffixes after environment substitution: %s == %s == %s" % (s_dict[s_k][0], k, s_k))
+ s_dict[s_k] = (k,v)
+ try:
+ return s_dict[ext][1]
+ except KeyError:
+ raise UserError("While building `%s': Don't know how to build a file with suffix %s." % (repr(map(str, target)), repr(ext)))
+
def __cmp__(self, other):
return cmp(self.action_dict, other.action_dict)
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index d486e70..e8a6199 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -73,6 +73,8 @@ class Environment:
try:
if s[0] == '$':
return self.d.get(s[1:], '')
+ if s[1] == '$':
+ return s[0] + self.d.get(s[2:], '')
except IndexError:
pass
return self.d.get(s, s)
@@ -444,8 +446,12 @@ class BuilderTestCase(unittest.TestCase):
def func_action(target, source, env):
return 0
+ env['BAR_SUFFIX'] = '.BAR2'
+ env['FOO_SUFFIX'] = '.FOO2'
builder = SCons.Builder.Builder(action={ '.foo' : func_action,
- '.bar' : func_action })
+ '.bar' : func_action,
+ '$BAR_SUFFIX' : func_action,
+ '$FOO_SUFFIX' : func_action })
assert isinstance(builder, SCons.Builder.CompositeBuilder)
assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
@@ -465,6 +471,27 @@ class BuilderTestCase(unittest.TestCase):
match = str(e) == "While building `['test3']' from `test1.foo': Cannot build multiple sources with different extensions: .bar, .foo"
assert match, e
+ tgt = builder(env, target='test4', source=['test4.BAR2'])
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+ try:
+ tgt.build()
+ flag = 1
+ except SCons.Errors.UserError, e:
+ print e
+ flag = 0
+ assert flag, "It should be possible to define actions in composite builders using variables."
+ env['FOO_SUFFIX'] = '.BAR2'
+ builder.add_action('$NEW_SUFFIX', func_action)
+ flag = 0
+ tgt = builder(env, target='test5', source=['test5.BAR2'])
+ try:
+ tgt.build()
+ except SCons.Errors.UserError:
+ flag = 1
+ assert flag, "UserError should be thrown when we build targets with ambigous suffixes."
+ del env.d['FOO_SUFFIX']
+ del env.d['BAR_SUFFIX']
+
foo_bld = SCons.Builder.Builder(action = 'a-foo',
src_suffix = '.ina',
suffix = '.foo')
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index aa0fbd9..2cef6fa 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -170,6 +170,7 @@ def SharedObject():
ProgScan = SCons.Scanner.Prog.ProgScan()
StaticLibrary = SCons.Builder.Builder(action=[ StaticCheck, "$ARCOM" ],
+ emitter='$LIBEMITTER',
prefix = '$LIBPREFIX',
suffix = '$LIBSUFFIX',
src_suffix = '$OBJSUFFIX',
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 2d6e0bc..f4d4845 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -630,6 +630,44 @@ class NodeTestCase(unittest.TestCase):
assert s.called
assert node.implicit == [d], node.implicit
+ # Check that scanning a node with some stored implicit
+ # dependencies resets internal attributes appropriately
+ # if the stored dependencies need recalculation.
+ class StoredNode(MyNode):
+ def get_stored_implicit(self):
+ return ['implicit1', 'implicit2']
+
+ class NotCurrent:
+ def current(self, node, sig):
+ return None
+ def bsig(self, node):
+ return 0
+
+ import SCons.Sig
+
+ save_default_calc = SCons.Sig.default_calc
+ save_implicit_cache = SCons.Node.implicit_cache
+ save_implicit_deps_changed = SCons.Node.implicit_deps_changed
+ save_implicit_deps_unchanged = SCons.Node.implicit_deps_unchanged
+ SCons.Sig.default_calc = NotCurrent()
+ SCons.Node.implicit_cache = 1
+ SCons.Node.implicit_deps_changed = None
+ SCons.Node.implicit_deps_unchanged = None
+ try:
+ sn = StoredNode("eee")
+ sn._children = ['fake']
+ sn.target_scanner = s
+
+ sn.scan()
+
+ assert sn.implicit == [], sn.implicit
+ assert not hasattr(sn, '_children'), "unexpected _children attribute"
+ finally:
+ SCons.Sig.default_calc = save_default_calc
+ SCons.Node.implicit_cache = save_implicit_cache
+ SCons.Node.implicit_deps_changed = save_implicit_deps_changed
+ SCons.Node.implicit_deps_unchanged = save_implicit_deps_unchanged
+
def test_scanner_key(self):
"""Test that a scanner_key() method exists"""
assert SCons.Node.Node().scanner_key() == None
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 1a80d5a..66ffe64 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -360,6 +360,7 @@ class Node:
return
self.implicit = []
self.implicit_dict = {}
+ self._children_reset()
if not self.has_builder():
return
@@ -377,6 +378,7 @@ class Node:
# and the bsig:
self.implicit = []
self.implicit_dict = {}
+ self._children_reset()
self.del_bsig()
build_env = self.get_build_env()
@@ -594,16 +596,19 @@ class Node:
added = 1
c.parents[self] = 1
if added:
- try:
- delattr(self, '_children')
- except AttributeError:
- pass
+ self._children_reset()
def add_wkid(self, wkid):
"""Add a node to the list of kids waiting to be evaluated"""
if self.wkids != None:
self.wkids.append(wkid)
+ def _children_reset(self):
+ try:
+ delattr(self, '_children')
+ except AttributeError:
+ pass
+
def children(self, scan=1):
"""Return a list of the node's direct children, minus those
that are ignored by this node."""
diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py
new file mode 100644
index 0000000..1febc31
--- /dev/null
+++ b/src/engine/SCons/Tool/qt.py
@@ -0,0 +1,214 @@
+"""SCons.Tool.qt
+
+Tool-specific initialization for qt.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os.path
+import re
+
+import SCons.Tool
+import SCons.Defaults
+
+header_extensions = (".h", ".H", ".hxx", ".hpp", ".hh")
+
+class _Automoc:
+ """
+ Callable class, which works as an emitter for Programs, SharedLibraries and
+ StaticLibraries.
+ """
+
+ def __init__(self, objBuilder,uicDeclBuild,mocFromHBld,mocFromCppBld):
+ self.objBuilder = objBuilder
+ self.uicDeclBld = uicDeclBuild
+ self.mocFromHBld = mocFromHBld
+ self.mocFromCppBld = mocFromCppBld
+
+ def __call__(self, target, source, env):
+ """
+ Smart autoscan function. Gets the list of objects for the Program
+ or Lib. Adds objects and builders for the special qt files.
+ """
+ # To make the following work, we assume that we stay in the
+ # root directory
+ old_os_cwd = os.getcwd()
+ old_fs_cwd = SCons.Node.FS.default_fs.getcwd()
+ SCons.Node.FS.default_fs.chdir(SCons.Node.FS.default_fs.Dir('#'),
+ change_os_dir=1)
+
+ # a regular expression for the Q_OBJECT macro
+ # currently fails, when Q_OBJECT is in comment (e.g. /* Q_OBJECT */)
+ q_object_search = re.compile(r'\sQ_OBJECT[\s;]')
+ # out_sources contains at least all sources for the Library or Prog
+ out_sources = source[:]
+ for s in source:
+ prefix, suffix = os.path.splitext(str(s))
+ # Nodes for header (h) / moc file (moc_cpp) / cpp file (cpp)
+ # and ui.h file (ui_h)
+ cpp = s.sources[0]
+ ui = None
+ if cpp.sources != None and len(cpp.sources) > 0:
+ src_src_suffix = os.path.splitext(str(cpp.sources[0]))[1]
+ if src_src_suffix == env.subst('$QT_UISUFFIX'):
+ ui = cpp.sources[0]
+
+ src_prefix, src_suffix = os.path.splitext(str(cpp.srcnode()))
+ h=None
+ for h_ext in header_extensions:
+ if os.path.exists(src_prefix + h_ext):
+ h = SCons.Node.FS.default_fs.File(prefix + h_ext)
+
+ if ui:
+ # file built from .ui file -> build also header from .ui
+ h = self.uicDeclBld(env, prefix, ui)
+ env.Depends(cpp, h)
+ ui_h_suff = env.subst('$QT_UIHSUFFIX')
+ if os.path.exists(src_prefix + ui_h_suff):
+ # if a .ui.h file exists, we need to specify the dependecy ...
+ ui_h = SCons.Node.FS.default_fs.File(prefix + ui_h_suff)
+ env.Depends(cpp, ui_h)
+ if (h and q_object_search.search(h.get_contents())) or ui:
+ # h file with the Q_OBJECT macro found -> add moc_cpp
+ dir,base = os.path.split(prefix)
+ src_ext = os.path.splitext(str(h))[1]
+ moc_cpp = SCons.Node.FS.default_fs.File(os.path.join(dir,
+ env['QT_MOCNAMEGENERATOR'](base, src_ext, env)))
+ moc_o = self.objBuilder(source=moc_cpp)
+ out_sources.append(moc_o)
+ self.objBuilder(moc_o, moc_cpp)
+ self.mocFromHBld(env, moc_cpp, h)
+ moc_cpp.target_scanner = SCons.Defaults.CScan
+ if cpp and q_object_search.search(cpp.get_contents()):
+ # cpp file with Q_OBJECT macro found -> add moc
+ # (to be included in cpp)
+ dir,base = os.path.split(prefix)
+ src_ext = os.path.splitext(str(cpp))[1]
+ moc = SCons.Node.FS.default_fs.File(os.path.join(dir,
+ env['QT_MOCNAMEGENERATOR'](base, src_ext, env)))
+ self.mocFromCppBld(env, moc, cpp)
+ env.Ignore(moc, moc)
+ moc.source_scanner = SCons.Defaults.CScan
+
+ os.chdir(old_os_cwd)
+ SCons.Node.FS.default_fs.chdir(old_fs_cwd)
+ return (target, out_sources)
+
+def _detect(env):
+ """Not really safe, but fast method to detect the QT library"""
+ QTDIR = None
+ if not QTDIR:
+ QTDIR = env.get('QTDIR',None)
+ if not QTDIR:
+ QTDIR = os.environ.get('QTDIR',None)
+ if not QTDIR:
+ moc = env.Detect('moc')
+ if moc:
+ QTDIR = dirname(dirname(moc))
+ else:
+ QTDIR = None
+ env['QTDIR'] = QTDIR
+ return QTDIR
+
+def generate(env):
+ """Add Builders and construction variables for qt to an Environment."""
+ _detect(env)
+ env['QT_MOC'] = os.path.join('$QTDIR','bin','moc')
+ env['QT_UIC'] = os.path.join('$QTDIR','bin','uic')
+ env['QT_LIB'] = 'qt'
+
+ # Some QT specific flags. I don't expect someone wants to
+ # manipulate those ...
+ env['QT_UICIMPLFLAGS'] = ''
+ env['QT_UICDECLFLAGS'] = ''
+ env['QT_MOCFROMHFLAGS'] = ''
+ env['QT_MOCFROMCXXFLAGS'] = '-i'
+
+ # Suffixes for the headers / sources to generate
+ env['QT_HSUFFIX'] = '.h'
+ env['QT_UISUFFIX'] = '.ui'
+ env['QT_UIHSUFFIX'] = '.ui.h'
+ env['QT_MOCNAMEGENERATOR'] = \
+ lambda x, src_suffix, env: 'moc_' + x + env.get('CXXFILESUFFIX','.cc')
+
+ # Commands for the qt support ...
+ # command to generate implementation (cpp) file from a .ui file
+ env['QT_UICIMPLCOM'] = ('$QT_UIC $QT_UICIMPLFLAGS -impl '
+ '${TARGETS[0].filebase}$QT_HSUFFIX '
+ '-o $TARGET $SOURCES')
+ # command to generate declaration (h) file from a .ui file
+ env['QT_UICDECLCOM'] = ('$QT_UIC $QT_UICDECLFLAGS '
+ '-o ${TARGETS[0].base}$QT_HSUFFIX $SOURCES')
+ # command to generate meta object information for a class declarated
+ # in a header
+ env['QT_MOCFROMHCOM'] = '$QT_MOC $QT_MOCFROMHFLAGS -o $TARGET $SOURCE'
+ # command to generate meta object information for a class declatazed
+ # in a cpp file
+ env['QT_MOCFROMCXXCOM'] = '$QT_MOC $QT_MOCFROMCXXFLAGS -o $TARGET $SOURCE'
+
+ # ... and the corresponding builders
+ uicDeclBld = SCons.Builder.Builder(action='$QT_UICDECLCOM',
+ src_suffix='$QT_UISUFFIX',
+ suffix='$QT_HSUFFIX')
+ mocFromHBld = SCons.Builder.Builder(action='$QT_MOCFROMHCOM',
+ src_suffix='$QT_HSUFFIX',
+ suffix='$QT_MOCSUFFIX')
+ mocFromCppBld = SCons.Builder.Builder(action='$QT_MOCFROMCXXCOM',
+ src_suffix='$QT_CXXSUFFIX',
+ suffix='$QT_MOCSUFFIX')
+
+ # we use CXXFile to generate .cpp files from .ui files
+ c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
+ cxx_file.add_action('$QT_UISUFFIX', '$QT_UICIMPLCOM')
+
+ # We use the emitters of Program / StaticLibrary / SharedLibrary
+ # to produce almost all builders except .cpp from .ui
+ try:
+ static = env.StaticObject
+ except AttributeError:
+ static = SCons.Defaults.StaticObject
+ try:
+ shared = env.SharedObject
+ except AttributeError:
+ shared = SCons.Defaults.SharedObject
+ env['PROGEMITTER'] = _Automoc(static,
+ uicDeclBld,mocFromHBld,mocFromCppBld)
+ env['SHLIBEMITTER'] = _Automoc(shared,
+ uicDeclBld,mocFromHBld,mocFromCppBld)
+ env['LIBEMITTER'] = _Automoc(static,
+ uicDeclBld,mocFromHBld,mocFromCppBld)
+ # Of course, we need to link against the qt libraries
+ env.Append(CPPPATH=os.path.join('$QTDIR', 'include'))
+ env.Append(LIBPATH=os.path.join('$QTDIR', 'lib'))
+ env.Append(LIBS='$QT_LIB')
+
+def exists(env):
+ return _detect(env)
diff --git a/test/QT.py b/test/QT.py
new file mode 100644
index 0000000..ec64d35
--- /dev/null
+++ b/test/QT.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Testing the 'qt' tool, i.e. support for .ui files and automatic
+generation of qt's moc files.
+"""
+
+import TestSCons
+import os.path
+
+python = TestSCons.python
+_exe = TestSCons._exe
+_dll = TestSCons._dll
+lib_ = TestSCons.lib_
+
+test = TestSCons.TestSCons()
+
+test.subdir( 'qt', ['qt', 'bin'], ['qt', 'include'], ['qt', 'lib'] )
+
+# create a dummy qt installation
+
+test.write(['qt', 'bin', 'mymoc.py'], """
+import getopt
+import sys
+import string
+import re
+cmd_opts, args = getopt.getopt(sys.argv[1:], 'io:', [])
+output = None
+impl = 0
+opt_string = ''
+for opt, arg in cmd_opts:
+ if opt == '-o': output = open(arg, 'wb')
+ elif opt == '-i': impl = 1
+ else: opt_string = opt_string + ' ' + opt
+for a in args:
+ contents = open(a, 'rb').read()
+ subst = r'{ my_qt_symbol( "' + a + '\\\\n" ); }'
+ if impl:
+ contents = re.sub( r'#include.*', '', contents )
+ output.write(string.replace(contents, 'Q_OBJECT', subst))
+output.close()
+sys.exit(0)
+""" )
+
+test.write(['qt', 'bin', 'myuic.py'], """
+import sys
+import string
+output_arg = 0
+impl_arg = 0
+impl = None
+source = None
+for arg in sys.argv[1:]:
+ if output_arg:
+ output = open(arg, 'wb')
+ output_arg = 0
+ elif impl_arg:
+ impl = arg
+ impl_arg = 0
+ elif arg == "-o":
+ output_arg = 1
+ elif arg == "-impl":
+ impl_arg = 1
+ else:
+ if source:
+ sys.exit(1)
+ source = open(arg, 'rb')
+if impl:
+ output.write( '#include "' + impl + '"\\n' )
+else:
+ output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" )
+output.close()
+sys.exit(0)
+""" )
+
+test.write(['qt', 'include', 'my_qobject.h'], r"""
+#define Q_OBJECT ;
+void my_qt_symbol(const char *arg);
+""")
+
+test.write(['qt', 'lib', 'my_qobject.cpp'], r"""
+#include "../include/my_qobject.h"
+#include <stdio.h>
+void my_qt_symbol(const char *arg) {
+ printf( arg );
+}
+""")
+
+test.write(['qt', 'lib', 'SConstruct'], r"""
+env = Environment()
+env.StaticLibrary( 'myqt', 'my_qobject.cpp' )
+""")
+
+test.run(chdir=test.workpath('qt','lib'), arguments = '.')
+
+QT = test.workpath('qt')
+QT_LIB = 'myqt'
+QT_MOC = '%s %s' % (python, test.workpath('qt','bin','mymoc.py'))
+QT_UIC = '%s %s' % (python, test.workpath('qt','bin','myuic.py'))
+
+# 3 test cases with 3 different operation modes
+
+def createSConstruct(test,place):
+ test.write(place, """
+env = Environment(QTDIR='%s',
+ QT_LIB='%s',
+ QT_MOC = '%s',
+ QT_UIC = '%s',
+ tools=['default','qt'])
+if ARGUMENTS.get('build_dir', 0):
+ if ARGUMENTS.get('chdir', 0):
+ SConscriptChdir(1)
+ else:
+ SConscriptChdir(0)
+ BuildDir('build', '.', duplicate=1)
+ sconscript = Dir('build').File('SConscript')
+else:
+ sconscript = File('SConscript')
+Export("env")
+SConscript( sconscript )
+""" % (QT, QT_LIB, QT_MOC, QT_UIC))
+
+test.subdir( 'work1', 'work2', 'work3', 'work4' )
+
+# 1. create a moc file from a header file.
+
+aaa_exe = 'aaa' + _exe
+moc = 'moc_aaa.cc'
+
+createSConstruct(test, ['work1', 'SConstruct'])
+test.write( ['work1', 'SConscript'], """
+Import("env")
+env.Program(target = 'aaa', source = 'aaa.cpp')
+""")
+
+test.write(['work1', 'aaa.cpp'], r"""
+#include "aaa.h"
+int main() { aaa(); return 0; }
+""")
+
+test.write(['work1', 'aaa.h'], r"""
+#include "my_qobject.h"
+void aaa(void) Q_OBJECT;
+""")
+
+test.run(chdir='work1', arguments = aaa_exe)
+test.up_to_date(chdir='work1', options = '-n', arguments=aaa_exe)
+
+test.up_to_date(chdir='work1', options = '-n', arguments = aaa_exe)
+test.write(['work1', 'aaa.h'], r"""
+/* a change */
+#include "my_qobject.h"
+void aaa(void) Q_OBJECT;
+""")
+test.not_up_to_date(chdir='work1', options='-n', arguments = moc)
+test.run(program = test.workpath('work1', aaa_exe), stdout = 'aaa.h\n')
+
+test.run(chdir='work1',
+ arguments = "build_dir=1 " +
+ test.workpath('work1', 'build', aaa_exe) )
+test.run(chdir='work1',
+ arguments = "build_dir=1 chdir=1 " +
+ test.workpath('work1', 'build', aaa_exe) )
+
+test.fail_test( not os.path.exists(test.workpath('work1', 'build', moc)) )
+
+# 2. create .cpp, .h, moc_....cpp from .ui file
+
+aaa_dll = lib_ + 'aaa' + _dll
+moc = 'moc_aaa.cc'
+cpp = 'aaa.cc'
+h = 'aaa.h'
+
+createSConstruct(test, ['work2', 'SConstruct'])
+test.write(['work2', 'SConscript'], """
+Import("env")
+env.SharedLibrary(target = 'aaa', source = ['aaa.ui', 'useit.cpp'])
+""")
+
+test.write(['work2', 'aaa.ui'], r"""
+void aaa(void)
+""")
+
+test.write(['work2', 'useit.cpp'], r"""
+#include "aaa.h"
+void useit() {
+ aaa();
+}
+""")
+
+test.run(chdir='work2', arguments = aaa_dll)
+test.up_to_date(chdir='work2', options='-n',arguments = aaa_dll)
+test.write(['work2', 'aaa.ui'], r"""
+/* a change */
+void aaa(void)
+""")
+test.not_up_to_date(chdir='work2', options = '-n', arguments = moc)
+test.not_up_to_date(chdir='work2', options = '-n', arguments = cpp)
+test.not_up_to_date(chdir='work2', options = '-n', arguments = h)
+test.run(chdir='work2', arguments = aaa_dll)
+test.write(['work2', 'aaa.ui.h'], r"""
+/* test dependency to .ui.h */
+""")
+test.not_up_to_date(chdir='work2', options = '-n', arguments = cpp)
+test.up_to_date(chdir='work2', options = '-n', arguments = h)
+test.up_to_date(chdir='work2', options = '-n', arguments = moc)
+
+test.run(chdir='work2',
+ arguments = "build_dir=1 " +
+ test.workpath('work2', 'build', aaa_dll) )
+test.run(chdir='work2',
+ arguments = "build_dir=1 chdir=1 " +
+ test.workpath('work2', 'build', aaa_dll) )
+
+test.fail_test(not os.path.exists(test.workpath('work2','build',moc)) or
+ not os.path.exists(test.workpath('work2','build',cpp)) or
+ not os.path.exists(test.workpath('work2','build',h)))
+
+# 3. create a moc file from a cpp file
+
+lib_aaa = lib_ + 'aaa.a'
+moc = 'moc_aaa.cc'
+
+createSConstruct(test, ['work3', 'SConstruct'])
+test.write(['work3', 'SConscript'], """
+Import("env")
+env.StaticLibrary(target = '%s', source = ['aaa.cpp','useit.cpp'])
+""" % lib_aaa)
+
+test.write(['work3', 'aaa.h'], r"""
+void aaa(void);
+""")
+
+test.write(['work3', 'aaa.cpp'], r"""
+#include "my_qobject.h"
+void aaa(void) Q_OBJECT
+#include "%s"
+""" % moc)
+
+test.write(['work3', 'useit.cpp'], r"""
+#include "aaa.h"
+void useit() {
+ aaa();
+}
+""")
+
+test.run(chdir='work3', arguments = lib_aaa)
+test.up_to_date(chdir='work3', options = '-n', arguments = lib_aaa)
+test.write(['work3', 'aaa.cpp'], r"""
+#include "my_qobject.h"
+/* a change */
+void aaa(void) Q_OBJECT
+#include "%s"
+""" % moc)
+test.not_up_to_date(chdir='work3', options = '-n', arguments = moc)
+
+test.run(chdir='work3',
+ arguments = "build_dir=1 " +
+ test.workpath('work3', 'build', lib_aaa) )
+test.run(chdir='work3',
+ arguments = "build_dir=1 chdir=1 " +
+ test.workpath('work3', 'build', lib_aaa) )
+
+test.fail_test(not os.path.exists(test.workpath('work3', 'build', moc)))
+
+
+# look if qt is installed, and try out all builders
+
+if os.environ.get('QTDIR', None):
+
+ test.write( ['work4', 'SConstruct'],"""
+import os
+env = Environment(tools=['default','qt'], CXXFILESUFFIX=".cpp")
+env.Program('test_realqt', ['mocFromCpp.cpp',
+ 'mocFromH.cpp',
+ 'anUiFile.ui',
+ 'main.cpp'])
+""")
+
+ test.write( ['work4', 'mocFromCpp.h'],"""
+void mocFromCpp();
+""")
+
+ test.write( ['work4', 'mocFromCpp.cpp'],"""
+#include <qobject.h>
+#include "mocFromCpp.h"
+class MyClass1 : public QObject {
+ Q_OBJECT
+ public:
+ MyClass1() : QObject() {};
+ public slots:
+ void myslot() {};
+};
+void mocFromCpp() {
+ MyClass1 myclass;
+}
+#include "moc_mocFromCpp.cpp"
+""")
+
+ test.write( ['work4', 'mocFromH.h'],"""
+#include <qobject.h>
+class MyClass2 : public QObject {
+ Q_OBJECT;
+ public:
+ MyClass2();
+ public slots:
+ void myslot();
+};
+void mocFromH();
+""")
+
+ test.write( ['work4', 'mocFromH.cpp'],"""
+#include "mocFromH.h"
+
+MyClass2::MyClass2() : QObject() {}
+void MyClass2::myslot() {}
+void mocFromH() {
+ MyClass2 myclass;
+}
+""")
+
+ test.write( ['work4', 'anUiFile.ui'],"""
+<!DOCTYPE UI><UI>
+<class>MyWidget</class>
+<widget>
+ <class>QWidget</class>
+ <property name="name">
+ <cstring>MyWidget</cstring>
+ </property>
+ <property name="caption">
+ <string>MyWidget</string>
+ </property>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
+""")
+
+ test.write( ['work4', 'main.cpp'], """
+#include "mocFromCpp.h"
+#include "mocFromH.h"
+#include "anUiFile.h"
+int main() {
+ mocFromCpp();
+ mocFromH();
+ MyWidget mywidget();
+}
+""")
+
+ test.run(chdir='work4', arguments="test_realqt" + _exe)
+else:
+ print "Could not find QT, skipping test(s)."
+
+
+test.pass_test()
diff --git a/test/QTFLAGS.py b/test/QTFLAGS.py
new file mode 100644
index 0000000..c61fb9c
--- /dev/null
+++ b/test/QTFLAGS.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Testing the configuration mechanisms of the 'qt' tool.
+"""
+
+import TestSCons
+import os.path
+
+python = TestSCons.python
+_exe = TestSCons._exe
+
+test = TestSCons.TestSCons()
+
+test.subdir( 'qt', ['qt', 'bin'], ['qt', 'include'], ['qt', 'lib'] )
+
+# create a dummy qt installation
+
+test.write(['qt', 'bin', 'mymoc.py'], """
+import getopt
+import sys
+import string
+import re
+cmd_opts, args = getopt.getopt(sys.argv[1:], 'wzio:', [])
+output = None
+impl = 0
+opt_string = ''
+for opt, arg in cmd_opts:
+ if opt == '-o': output = open(arg, 'wb')
+ elif opt == '-i': impl = 1
+ else: opt_string = opt_string + ' ' + opt
+output.write( "/* mymoc.py%s */\\n" % opt_string)
+for a in args:
+ contents = open(a, 'rb').read()
+ subst = r'{ my_qt_symbol( "' + a + '\\\\n" ); }'
+ if impl:
+ contents = re.sub( r'#include.*', '', contents )
+ output.write(string.replace(contents, 'Q_OBJECT', subst))
+output.close()
+sys.exit(0)
+""" )
+
+test.write(['qt', 'bin', 'myuic.py'], """
+import sys
+import string
+output_arg = 0
+impl_arg = 0
+impl = None
+source = None
+opt_string = ''
+for arg in sys.argv[1:]:
+ if output_arg:
+ output = open(arg, 'wb')
+ output_arg = 0
+ elif impl_arg:
+ impl = arg
+ impl_arg = 0
+ elif arg == "-o":
+ output_arg = 1
+ elif arg == "-impl":
+ impl_arg = 1
+ elif arg[0:1] == "-":
+ opt_string = opt_string + ' ' + arg
+ else:
+ if source:
+ sys.exit(1)
+ source = open(arg, 'rb')
+output.write("/* myuic.py%s */\\n" % opt_string)
+if impl:
+ output.write( '#include "' + impl + '"\\n' )
+else:
+ output.write( '#include "my_qobject.h"\\n' + source.read() + " Q_OBJECT \\n" )
+output.close()
+sys.exit(0)
+""" )
+
+test.write(['qt', 'include', 'my_qobject.h'], r"""
+#define Q_OBJECT ;
+void my_qt_symbol(const char *arg);
+""")
+
+test.write(['qt', 'lib', 'my_qobject.cpp'], r"""
+#include "../include/my_qobject.h"
+#include <stdio.h>
+void my_qt_symbol(const char *arg) {
+ printf( arg );
+}
+""")
+
+test.write(['qt', 'lib', 'SConstruct'], r"""
+env = Environment()
+env.StaticLibrary( 'myqt', 'my_qobject.cpp' )
+""")
+
+test.run(chdir=test.workpath('qt','lib'), arguments = '.')
+
+QT = test.workpath('qt')
+QT_LIB = 'myqt'
+QT_MOC = '%s %s' % (python, test.workpath('qt','bin','mymoc.py'))
+QT_UIC = '%s %s' % (python, test.workpath('qt','bin','myuic.py'))
+
+# 3 test cases with 3 different operation modes
+
+def createSConstruct(test,place,overrides):
+ test.write(place, """
+env = Environment(QTDIR='%s',
+ QT_LIB='%s',
+ QT_MOC = '%s',
+ QT_UIC = '%s',
+ %s
+ tools=['default','qt'])
+if ARGUMENTS.get('build_dir', 0):
+ if ARGUMENTS.get('chdir', 0):
+ SConscriptChdir(1)
+ else:
+ SConscriptChdir(0)
+ BuildDir('build', '.', duplicate=1)
+ sconscript = Dir('build').File('SConscript')
+else:
+ sconscript = File('SConscript')
+Export("env")
+SConscript( sconscript )
+""" % (QT, QT_LIB, QT_MOC, QT_UIC, overrides))
+
+
+createSConstruct(test, ['SConstruct'],
+ """QT_UICIMPLFLAGS='-x',
+ QT_UICDECLFLAGS='-y',
+ QT_MOCFROMHFLAGS='-z',
+ QT_MOCFROMCXXFLAGS='-i -w',
+ QT_HSUFFIX='.hpp',
+ QT_MOCNAMEGENERATOR=lambda x,src_suffix,env: x + '.moc.cpp',
+ QT_UISUFFIX='.des',
+ QT_UIHSUFFUX='.des.hpp',
+ CXXFILESUFFIX='.cpp',""")
+test.write('SConscript',"""
+Import("env")
+env.Program('mytest', ['mocFromH.cpp',
+ 'mocFromCpp.cpp',
+ 'an_ui_file.des',
+ 'another_ui_file.des',
+ 'main.cpp'])
+""")
+
+test.write('mocFromH.hpp', """
+#include "my_qobject.h"
+void mocFromH() Q_OBJECT
+""")
+
+test.write('mocFromH.cpp', """
+#include "mocFromH.hpp"
+""")
+
+test.write('mocFromCpp.cpp', """
+#include "my_qobject.h"
+void mocFromCpp() Q_OBJECT
+#include "mocFromCpp.moc.cpp"
+""")
+
+test.write('an_ui_file.des', """
+void an_ui_file()
+""")
+
+test.write('another_ui_file.des', """
+void another_ui_file()
+""")
+
+test.write('another_ui_file.desc.hpp', """
+/* just a dependency checker */
+""")
+
+test.write('main.cpp', """
+#include "mocFromH.hpp"
+#include "an_ui_file.hpp"
+#include "another_ui_file.hpp"
+void mocFromCpp();
+
+int main() {
+ mocFromH();
+ mocFromCpp();
+ an_ui_file();
+ another_ui_file();
+}
+""")
+
+test.run( arguments = "mytest" + _exe )
+
+def _existAll( test, files ):
+ return reduce(lambda x,y: x and y,
+ map(os.path.exists,map(test.workpath, files)))
+
+test.fail_test(not _existAll(test, ['mocFromH.moc.cpp',
+ 'mocFromCpp.moc.cpp',
+ 'an_ui_file.cpp',
+ 'an_ui_file.hpp',
+ 'an_ui_file.moc.cpp',
+ 'another_ui_file.cpp',
+ 'another_ui_file.hpp',
+ 'another_ui_file.moc.cpp']))
+
+def _flagTest(test,fileToContentsStart):
+ import string
+ for f,c in fileToContentsStart.items():
+ if string.find(test.read(f), c) != 0:
+ return 1
+ return 0
+
+test.fail_test(_flagTest(test, {'mocFromH.moc.cpp':'/* mymoc.py -z */',
+ 'mocFromCpp.moc.cpp':'/* mymoc.py -w */',
+ 'an_ui_file.cpp':'/* myuic.py -x */',
+ 'an_ui_file.hpp':'/* myuic.py -y */',
+ 'an_ui_file.moc.cpp':'/* mymoc.py -z */'}))
+
+test.pass_test()
diff --git a/test/import.py b/test/import.py
index d66f8e5..99aa120 100644
--- a/test/import.py
+++ b/test/import.py
@@ -89,6 +89,7 @@ tools = [
'pdftex',
'Perforce',
'RCS',
+ 'qt',
'rmic',
'SCCS',
'sgiar',