summaryrefslogtreecommitdiffstats
path: root/src/engine
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 /src/engine
parent5d238601a22fa009de4df38e0257c405f9b243db (diff)
downloadSCons-7a3d5b8e06e44fee43fc78d7eb72b8d271022c18.zip
SCons-7a3d5b8e06e44fee43fc78d7eb72b8d271022c18.tar.gz
SCons-7a3d5b8e06e44fee43fc78d7eb72b8d271022c18.tar.bz2
Add a QT tool. (Christoph Wiedemann)
Diffstat (limited to 'src/engine')
-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
7 files changed, 308 insertions, 8 deletions
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)