diff options
Diffstat (limited to 'src/engine/SCons')
-rw-r--r-- | src/engine/SCons/Builder.py | 20 | ||||
-rw-r--r-- | src/engine/SCons/BuilderTests.py | 29 | ||||
-rw-r--r-- | src/engine/SCons/Defaults.py | 1 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 38 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 13 | ||||
-rw-r--r-- | src/engine/SCons/Tool/qt.py | 214 |
6 files changed, 307 insertions, 8 deletions
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) |