summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-05-19 17:49:55 (GMT)
committerSteven Knight <knight@baldmt.com>2004-05-19 17:49:55 (GMT)
commit04ebe107191659f805bd63148c61c1026efeb686 (patch)
tree9122729d1120c1119fe10ff360f4675789ca8f9b /src
parent12d2ae1193b9e32e0164218bb73240791914f976 (diff)
downloadSCons-04ebe107191659f805bd63148c61c1026efeb686.zip
SCons-04ebe107191659f805bd63148c61c1026efeb686.tar.gz
SCons-04ebe107191659f805bd63148c61c1026efeb686.tar.bz2
Fix spurious rebuilds/reinstalls of header files and circular dependencies with generated header files by allowing Scanners to be associated explicitly with Builders, not just through Scanner file suffix lists.
Diffstat (limited to 'src')
-rw-r--r--src/CHANGES.txt8
-rw-r--r--src/engine/SCons/Builder.py39
-rw-r--r--src/engine/SCons/BuilderTests.py27
-rw-r--r--src/engine/SCons/Defaults.py13
-rw-r--r--src/engine/SCons/Environment.py5
-rw-r--r--src/engine/SCons/Node/FS.py9
-rw-r--r--src/engine/SCons/Node/FSTests.py8
-rw-r--r--src/engine/SCons/Node/NodeTests.py74
-rw-r--r--src/engine/SCons/Node/__init__.py77
-rw-r--r--src/engine/SCons/SConf.py11
-rw-r--r--src/engine/SCons/SConfTests.py2
-rw-r--r--src/engine/SCons/Scanner/CTests.py3
-rw-r--r--src/engine/SCons/Scanner/FortranTests.py3
-rw-r--r--src/engine/SCons/Scanner/IDLTests.py3
-rw-r--r--src/engine/SCons/Scanner/ScannerTests.py73
-rw-r--r--src/engine/SCons/Scanner/__init__.py35
-rw-r--r--src/engine/SCons/Taskmaster.py16
-rw-r--r--src/engine/SCons/TaskmasterTests.py17
-rw-r--r--src/engine/SCons/Tool/__init__.py10
-rw-r--r--src/engine/SCons/Tool/mingw.py2
-rw-r--r--src/engine/SCons/Tool/msvc.py2
-rw-r--r--src/engine/SCons/Tool/qt.py4
22 files changed, 328 insertions, 113 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index b1d3119..7aaabf5 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -119,6 +119,14 @@ RELEASE 0.96 - XXX
the parents's implicit dependencies, let returning up the normal
Taskmaster descent take care of it for us.
+ - Add documented support for separate target_scanner and source_scanner
+ arguments to Builder creation, which allows different scanners to
+ be applied to source files
+
+ - Don't re-install or (re-generate) .h files when a subsidiary #included
+ .h file changes. This eliminates incorrect circular dependencies
+ with .h files generated from other source files.
+
From Gary Oberbrunner:
- Add a --debug=presub option to print actions prior to substitution.
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 5c75a0f..4aa518f 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -300,7 +300,7 @@ def _init_nodes(builder, env, overrides, tlist, slist):
elif t.overrides != overrides:
raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
- elif builder.scanner and t.target_scanner and builder.scanner != t.target_scanner:
+ elif builder.target_scanner and t.target_scanner and builder.target_scanner != t.target_scanner:
raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
if builder.multi:
@@ -338,15 +338,22 @@ def _init_nodes(builder, env, overrides, tlist, slist):
t.env_set(env)
t.add_source(slist)
t.set_executor(executor)
- if builder.scanner:
- t.target_scanner = builder.scanner
- if not t.source_scanner:
- t.source_scanner = env.get_scanner(t.scanner_key())
-
- # Last, add scanners from the Environment to the source Nodes.
+ if builder.target_scanner:
+ t.target_scanner = builder.target_scanner
+ if t.source_scanner is None:
+ t.source_scanner = builder.source_scanner
+
+ # Add backup source scanners from the environment to the source
+ # nodes. This may not be necessary if the node will have a real
+ # source scanner added later (which is why these are the "backup"
+ # source scanners, not the real ones), but because source nodes may
+ # be used multiple times for different targets, it ends up being
+ # more efficient to do this calculation once here, as opposed to
+ # delaying it until later when we potentially have to calculate it
+ # over and over and over.
for s in slist:
- if not s.source_scanner:
- s.source_scanner = env.get_scanner(s.scanner_key())
+ if s.source_scanner is None and s.backup_source_scanner is None:
+ s.backup_source_scanner = env.get_scanner(s.scanner_key())
class EmitterProxy:
"""This is a callable class that can act as a
@@ -389,7 +396,8 @@ class BuilderBase:
src_suffix = '',
target_factory = SCons.Node.FS.default_fs.File,
source_factory = SCons.Node.FS.default_fs.File,
- scanner = None,
+ target_scanner = None,
+ source_scanner = None,
emitter = None,
multi = 0,
env = None,
@@ -416,7 +424,8 @@ class BuilderBase:
self.target_factory = target_factory
self.source_factory = source_factory
- self.scanner = scanner
+ self.target_scanner = target_scanner
+ self.source_scanner = source_scanner
self.emitter = emitter
@@ -592,7 +601,8 @@ class ListBuilder(SCons.Util.Proxy):
if __debug__: logInstanceCreation(self)
SCons.Util.Proxy.__init__(self, builder)
self.builder = builder
- self.scanner = builder.scanner
+ self.target_scanner = builder.target_scanner
+ self.source_scanner = builder.source_scanner
self.env = env
self.tlist = tlist
self.multi = builder.multi
@@ -628,12 +638,13 @@ class MultiStepBuilder(BuilderBase):
src_suffix = '',
target_factory = SCons.Node.FS.default_fs.File,
source_factory = SCons.Node.FS.default_fs.File,
- scanner=None,
+ target_scanner = None,
+ source_scanner = None,
emitter=None):
if __debug__: logInstanceCreation(self)
BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
target_factory, source_factory,
- scanner, emitter)
+ target_scanner, source_scanner, emitter)
if not SCons.Util.is_List(src_builder):
src_builder = [ src_builder ]
self.src_builder = src_builder
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index e33d95c..e592250 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -133,6 +133,7 @@ class MyNode_without_target_from_source:
self.builder = None
self.side_effect = 0
self.source_scanner = None
+ self.backup_source_scanner = None
def __str__(self):
return self.name
def builder_set(self, builder):
@@ -753,25 +754,30 @@ class BuilderTestCase(unittest.TestCase):
match = str(e) == "While building `['t8']': Don't know how to build a file with suffix `.unknown'."
assert match, e
- def test_build_scanner(self):
- """Testing ability to set a target scanner through a builder."""
+ def test_target_scanner(self):
+ """Testing ability to set target and source scanners through a builder."""
global instanced
class TestScanner:
pass
- scn = TestScanner()
+ tscan = TestScanner()
+ sscan = TestScanner()
env = Environment()
- builder = SCons.Builder.Builder(scanner=scn)
+ builder = SCons.Builder.Builder(target_scanner=tscan,
+ source_scanner=sscan)
tgt = builder(env, target='foo2', source='bar')
- assert tgt.target_scanner == scn, tgt.target_scanner
+ assert tgt.target_scanner == tscan, tgt.target_scanner
+ assert tgt.source_scanner == sscan, tgt.source_scanner
builder1 = SCons.Builder.Builder(action='foo',
src_suffix='.bar',
suffix='.foo')
builder2 = SCons.Builder.Builder(action='foo',
src_builder = builder1,
- scanner = scn)
+ target_scanner = tscan,
+ source_scanner = tscan)
tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')
- assert tgt.target_scanner == scn, tgt.target_scanner
+ assert tgt.target_scanner == tscan, tgt.target_scanner
+ assert tgt.source_scanner == tscan, tgt.source_scanner
def test_src_scanner(slf):
"""Testing ability to set a source file scanner through a builder."""
@@ -784,12 +790,14 @@ class BuilderTestCase(unittest.TestCase):
scanner = TestScanner()
builder = SCons.Builder.Builder(action='action')
- # With no scanner specified, source_scanner is None.
+ # With no scanner specified, source_scanner and
+ # backup_source_scanner are None.
env1 = Environment()
tgt = builder(env1, target='foo1.x', source='bar.y')
src = tgt.sources[0]
assert tgt.target_scanner != scanner, tgt.target_scanner
assert src.source_scanner is None, src.source_scanner
+ assert src.backup_source_scanner is None, src.backup_source_scanner
# Later use of the same source file with an environment that
# has a scanner must still set the scanner.
@@ -798,7 +806,8 @@ class BuilderTestCase(unittest.TestCase):
tgt = builder(env2, target='foo2.x', source='bar.y')
src = tgt.sources[0]
assert tgt.target_scanner != scanner, tgt.target_scanner
- assert src.source_scanner == scanner, src.source_scanner
+ assert src.source_scanner is None, src.source_scanner
+ assert src.backup_source_scanner == scanner, src.backup_source_scanner
def test_Builder_Args(self):
"""Testing passing extra args to a builder."""
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index 7a47b21..4c42f28 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -96,6 +96,8 @@ def SharedFlagChecker(source, target, env):
SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
# Scanners and suffixes for common languages.
+ObjSourceScan = SCons.Scanner.Scanner({})
+
CScan = SCons.Scanner.C.CScan()
CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
@@ -103,14 +105,23 @@ CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
".F", ".fpp", ".FPP",
".S", ".spp", ".SPP"]
+for suffix in CSuffixes:
+ ObjSourceScan.add_scanner(suffix, CScan)
+
DScan = SCons.Scanner.D.DScan()
DSuffixes = ['.d']
+for suffix in DSuffixes:
+ ObjSourceScan.add_scanner(suffix, DScan)
+
FortranScan = SCons.Scanner.Fortran.FortranScan()
FortranSuffixes = [".f", ".F", ".for", ".FOR"]
+for suffix in FortranSuffixes:
+ ObjSourceScan.add_scanner(suffix, FortranScan)
+
IDLSuffixes = [".idl", ".IDL"]
# Actions for common languages.
@@ -330,7 +341,7 @@ class NullCmdGenerator:
ConstructionEnvironment = {
'BUILDERS' : {},
- 'SCANNERS' : [CScan, FortranScan, DScan],
+ 'SCANNERS' : [],
'CPPSUFFIXES': CSuffixes,
'DSUFFIXES' : DSuffixes,
'FORTRANSUFFIXES': FortranSuffixes,
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index b920643..d43a99f 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -395,6 +395,7 @@ class Base:
try:
scanners = self._dict['SCANNERS']
except KeyError:
+ self.scanner_map = {}
return None
else:
self.scanner_map = sm = {}
@@ -402,6 +403,8 @@ class Base:
# claim they can scan the same suffix, earlier scanners
# in the list will overwrite later scanners, so that
# the result looks like a "first match" to the user.
+ if not SCons.Util.is_List(scanners):
+ scanners = [scanners]
scanners.reverse()
for scanner in scanners:
for k in scanner.get_skeys(self):
@@ -1159,7 +1162,7 @@ class Base:
arg = self.subst(arg)
nargs.append(arg)
nkw = self.subst_kw(kw)
- return apply(SCons.Scanner.Base, nargs, nkw)
+ return apply(SCons.Scanner.Scanner, nargs, nkw)
def SConsignFile(self, name=".sconsign", dbm_module=None):
name = self.subst(name)
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 15434cb..0d158a1 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -488,12 +488,6 @@ class Base(SCons.Node.Node):
self._rexists = self.rfile().exists()
return self._rexists
- def get_parents(self):
- parents = SCons.Node.Node.get_parents(self)
- if self.dir and not isinstance(self.dir, ParentOfRoot):
- parents.append(self.dir)
- return parents
-
def is_under(self, dir):
if self is dir:
return 1
@@ -1117,8 +1111,7 @@ class Dir(Base):
self.abspath_ = self.abspath + os.sep
self.repositories = []
self.srcdir = None
- self.source_scanner = None
-
+
self.entries = {}
self.entries['.'] = self
self.entries['..'] = self.dir
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index fb2e0fa..f69dd4e 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -60,6 +60,8 @@ class Scanner:
return [self.node]
def __hash__(self):
return self.hash
+ def select(self, node):
+ return self
class Environment:
def __init__(self):
@@ -1034,12 +1036,6 @@ class FSTestCase(unittest.TestCase):
skey = fs.Dir('ddd.x').scanner_key()
assert skey is None, skey
- d1 = fs.Dir('dir')
- f1 = fs.File('dir/file')
- assert f1.dir == d1, f1.dir
- parents = f1.get_parents()
- assert parents == [ d1 ], parents
-
test.write("i_am_not_a_directory", "\n")
try:
exc_caught = 0
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 535c84e..e36d6ce 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -138,6 +138,8 @@ class Scanner:
def __call__(self, node):
self.called = 1
return node.found_includes
+ def select(self, node):
+ return self
class MyNode(SCons.Node.Node):
"""The base Node class contains a number of do-nothing methods that
@@ -543,12 +545,6 @@ class NodeTestCase(unittest.TestCase):
node.add_dependency([three, four, one])
assert node.depends == [zero, one, two, three, four]
- assert zero.get_parents() == [node]
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
-
try:
node.add_depends([[five, six]])
except:
@@ -556,8 +552,6 @@ class NodeTestCase(unittest.TestCase):
else:
raise "did not catch expected exception"
assert node.depends == [zero, one, two, three, four]
- assert five.get_parents() == []
- assert six.get_parents() == []
def test_add_source(self):
@@ -583,12 +577,6 @@ class NodeTestCase(unittest.TestCase):
node.add_source([three, four, one])
assert node.sources == [zero, one, two, three, four]
- assert zero.get_parents() == [node]
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
-
try:
node.add_source([[five, six]])
except:
@@ -596,8 +584,6 @@ class NodeTestCase(unittest.TestCase):
else:
raise "did not catch expected exception"
assert node.sources == [zero, one, two, three, four]
- assert five.get_parents() == []
- assert six.get_parents() == []
def test_add_ignore(self):
"""Test adding files whose dependencies should be ignored.
@@ -622,12 +608,6 @@ class NodeTestCase(unittest.TestCase):
node.add_ignore([three, four, one])
assert node.ignore == [zero, one, two, three, four]
- assert zero.get_parents() == [node]
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
-
try:
node.add_ignore([[five, six]])
except:
@@ -635,8 +615,6 @@ class NodeTestCase(unittest.TestCase):
else:
raise "did not catch expected exception"
assert node.ignore == [zero, one, two, three, four]
- assert five.get_parents() == []
- assert six.get_parents() == []
def test_get_found_includes(self):
"""Test the default get_found_includes() method
@@ -689,6 +667,33 @@ class NodeTestCase(unittest.TestCase):
deps = node.get_implicit_deps(env, s, target)
assert deps == [d, e, f], map(str, deps)
+ def test_get_source_scanner(self):
+ """Test fetching the source scanner for a Node
+ """
+ class Builder:
+ pass
+ target = SCons.Node.Node()
+ source = SCons.Node.Node()
+ s = target.get_source_scanner(source)
+ assert s is None, s
+
+ ts1 = Scanner()
+ ts2 = Scanner()
+ ts3 = Scanner()
+
+ source.backup_source_scanner = ts1
+ s = target.get_source_scanner(source)
+ assert s is ts1, s
+
+ source.builder = Builder()
+ source.builder.source_scanner = ts2
+ s = target.get_source_scanner(source)
+ assert s is ts2, s
+
+ target.source_scanner = ts3
+ s = target.get_source_scanner(source)
+ assert s is ts3, s
+
def test_scan(self):
"""Test Scanner functionality
"""
@@ -947,6 +952,7 @@ class NodeTestCase(unittest.TestCase):
n.includes = 'testincludes'
n.found_include = {'testkey':'testvalue'}
n.implicit = 'testimplicit'
+ n.waiting_parents = ['foo', 'bar']
n.clear()
@@ -955,6 +961,7 @@ class NodeTestCase(unittest.TestCase):
assert n.includes is None, n.includes
assert n.found_includes == {}, n.found_includes
assert n.implicit is None, n.implicit
+ assert n.waiting_parents == [], n.waiting_parents
def test_get_subst_proxy(self):
"""Test the get_subst_proxy method."""
@@ -985,6 +992,25 @@ class NodeTestCase(unittest.TestCase):
n = SCons.Node.Node()
n.postprocess()
+ def test_add_to_waiting_parents(self):
+ """Test the add_to_waiting_parents() method"""
+ n1 = SCons.Node.Node()
+ n2 = SCons.Node.Node()
+ assert n1.waiting_parents == [], n1.waiting_parents
+ n1.add_to_waiting_parents(n2)
+ assert n1.waiting_parents == [n2], n1.waiting_parents
+
+ def test_call_for_all_waiting_parents(self):
+ """Test the call_for_all_waiting_parents() method"""
+ n1 = SCons.Node.Node()
+ n2 = SCons.Node.Node()
+ n1.add_to_waiting_parents(n2)
+ result = []
+ def func(node, result=result):
+ result.append(node)
+ n1.call_for_all_waiting_parents(func)
+ assert result == [n1, n2], result
+
if __name__ == "__main__":
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 2841759..75b3a6d 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -116,10 +116,11 @@ class Node:
self.ignore = [] # dependencies to ignore
self.ignore_dict = {}
self.implicit = None # implicit (scanned) dependencies (None means not scanned yet)
- self.parents = {}
+ self.waiting_parents = []
self.wkids = None # Kids yet to walk, when it's an array
self.target_scanner = None # explicit scanner from this node's Builder
- self.source_scanner = None # source scanner
+ self.source_scanner = None
+ self.backup_source_scanner = None
self.env = None
self.state = None
@@ -208,20 +209,29 @@ class Node:
else:
self.store_info(new_binfo)
- # Clear out the implicit dependency caches:
- # XXX this really should somehow be made more general and put
- # under the control of the scanners.
- if self.source_scanner:
- self.found_includes = {}
- self.includes = None
- for parent in self.get_parents():
- parent.implicit = None
- parent.del_binfo()
+ # Clear our scanned included files.
+ self.found_includes = {}
+ self.includes = None
+
+ # Clear the implicit dependency caches of any Nodes
+ # waiting for this Node to be built.
+ for parent in self.waiting_parents:
+ parent.implicit = None
+ parent.del_binfo()
+ self.waiting_parents = []
# The content just changed, delete any cached info
# so it will get recalculated.
self.del_cinfo()
+ def add_to_waiting_parents(self, node):
+ self.waiting_parents.append(node)
+
+ def call_for_all_waiting_parents(self, func):
+ func(self)
+ for parent in self.waiting_parents:
+ parent.call_for_all_waiting_parents(func)
+
def postprocess(self):
"""Clean up anything we don't need to hang onto after we've
been built."""
@@ -248,6 +258,8 @@ class Node:
self.found_includes = {}
self.implicit = None
+ self.waiting_parents = []
+
def visited(self):
"""Called just after this node has been visited
without requiring a build.."""
@@ -329,6 +341,10 @@ class Node:
if not scanner:
return []
+ # Give the scanner a chance to select a more specific scanner
+ # for this Node.
+ scanner = scanner.select(self)
+
try:
recurse = scanner.recursive
except AttributeError:
@@ -367,6 +383,22 @@ class Node:
self.implicit_factory_cache[path] = n
return n
+ def get_source_scanner(self, node):
+ """Fetch the source scanner for the specified node
+
+ NOTE: "self" is the target being built, "node" is
+ the source file for which we want to fetch the scanner.
+ """
+ if self.source_scanner:
+ return self.source_scanner
+ try:
+ scanner = node.builder.source_scanner
+ if scanner:
+ return scanner
+ except AttributeError:
+ pass
+ return node.backup_source_scanner or None
+
def scan(self):
"""Scan this node's dependents for implicit dependencies."""
# Don't bother scanning non-derived files, because we don't
@@ -405,20 +437,14 @@ class Node:
# self.del_binfo()
for child in self.children(scan=0):
- scanner = child.source_scanner
+ scanner = self.get_source_scanner(child)
if scanner:
- self._add_child(self.implicit,
- self.implicit_dict,
- child.get_implicit_deps(build_env,
- scanner,
- self))
+ deps = child.get_implicit_deps(build_env, scanner, self)
+ self._add_child(self.implicit, self.implicit_dict, deps)
# scan this node itself for implicit dependencies
- self._add_child(self.implicit,
- self.implicit_dict,
- self.get_implicit_deps(build_env,
- self.target_scanner,
- self))
+ deps = self.get_implicit_deps(build_env, self.target_scanner, self)
+ self._add_child(self.implicit, self.implicit_dict, deps)
# XXX See note above re: --implicit-cache.
#if implicit_cache:
@@ -632,7 +658,6 @@ class Node:
collection.append(c)
dict[c] = 1
added = 1
- c.parents[self] = 1
if added:
self._children_reset()
@@ -686,9 +711,6 @@ class Node:
else:
return self.sources + self.depends + self.implicit
- def get_parents(self):
- return self.parents.keys()
-
def set_state(self, state):
self.state = state
@@ -738,7 +760,8 @@ class Node:
if self.is_derived() and self.env:
env = self.get_build_env()
for s in self.sources:
- def f(node, env=env, scanner=s.source_scanner, target=self):
+ scanner = s.get_source_scanner(self)
+ def f(node, env=env, scanner=scanner, target=self):
return node.get_found_includes(env, scanner, target)
return SCons.Util.render_tree(s, f, 1)
else:
diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py
index db4e375..196b426 100644
--- a/src/engine/SCons/SConf.py
+++ b/src/engine/SCons/SConf.py
@@ -155,6 +155,17 @@ class SConf:
already_done.append( n )
self._setCache(n.children())
+ # Calling children() has set up the implicit cache (and
+ # other state), but we're not really building things yet,
+ # so generated files won't have been generated. Clear the
+ # state so we will, in fact, build everything that's necessary
+ # when we do the build.
+ #
+ # XXX - it would be good to find a better way to do this,
+ # maybe by doing something with the actions in the actual
+ # Taskmaster...?
+ n.clear()
+
def BuildNodes(self, nodes):
"""
Tries to build the given nodes immediately. Returns 1 on success,
diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py
index 8bc11ad..39af3a1 100644
--- a/src/engine/SCons/SConfTests.py
+++ b/src/engine/SCons/SConfTests.py
@@ -178,6 +178,8 @@ class SConfTestCase(unittest.TestCase):
return None
def postprocess(self):
pass
+ def clear(self):
+ pass
return [MyNode('n1'), MyNode('n2')]
self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()})
sconf.TryBuild(self.scons_env.SConfActionBuilder)
diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py
index f63060a..693ec18 100644
--- a/src/engine/SCons/Scanner/CTests.py
+++ b/src/engine/SCons/Scanner/CTests.py
@@ -193,6 +193,9 @@ class DummyEnvironment(UserDict.UserDict):
path = [path]
return map(self.subst, path)
+ def get_calculator(self):
+ return None
+
if os.path.normcase('foo') == os.path.normcase('FOO'):
my_normpath = os.path.normcase
else:
diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py
index e69f99e..766f0ee 100644
--- a/src/engine/SCons/Scanner/FortranTests.py
+++ b/src/engine/SCons/Scanner/FortranTests.py
@@ -168,6 +168,9 @@ class DummyEnvironment:
path = [path]
return map(self.subst, path)
+ def get_calculator(self):
+ return None
+
def deps_match(self, deps, headers):
scanned = map(os.path.normpath, map(str, deps))
expect = map(os.path.normpath, headers)
diff --git a/src/engine/SCons/Scanner/IDLTests.py b/src/engine/SCons/Scanner/IDLTests.py
index c20dedc..876f492 100644
--- a/src/engine/SCons/Scanner/IDLTests.py
+++ b/src/engine/SCons/Scanner/IDLTests.py
@@ -218,6 +218,9 @@ class DummyEnvironment:
def __delitem__(self,key):
del self.Dictionary()[key]
+ def get_calculator(self):
+ return None
+
global my_normpath
my_normpath = os.path.normpath
diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py
index 4e662a2..a0c6f01 100644
--- a/src/engine/SCons/Scanner/ScannerTests.py
+++ b/src/engine/SCons/Scanner/ScannerTests.py
@@ -65,6 +65,17 @@ class FindPathDirsTestCase(unittest.TestCase):
class ScannerTestCase(unittest.TestCase):
+ def test_creation(self):
+ """Test creation of Scanner objects through the Scanner() function"""
+ def func(self):
+ pass
+ s = SCons.Scanner.Scanner(func)
+ assert isinstance(s, SCons.Scanner.Base), s
+ s = SCons.Scanner.Scanner({})
+ assert isinstance(s, SCons.Scanner.Selector), s
+
+class BaseTestCase(unittest.TestCase):
+
def func(self, filename, env, target, *args):
self.filename = filename
self.env = env
@@ -192,6 +203,66 @@ class ScannerTestCase(unittest.TestCase):
self.failUnless(sk == ['.3', '.4'],
"sk was %s, not ['.3', '.4']")
+ def test_select(self):
+ """Test the Scanner.Base select() method"""
+ scanner = SCons.Scanner.Base(function = self.func)
+ s = scanner.select('.x')
+ assert s is scanner, s
+
+class SelectorTestCase(unittest.TestCase):
+ class skey:
+ def __init__(self, key):
+ self.key = key
+ def scanner_key(self):
+ return self.key
+
+ def test___init__(self):
+ """Test creation of Scanner.Selector object"""
+ s = SCons.Scanner.Selector({})
+ assert isinstance(s, SCons.Scanner.Selector), s
+ assert s.dict == {}, s.dict
+
+ def test___call__(self):
+ """Test calling Scanner.Selector objects"""
+ called = []
+ def s1func(node, env, path, called=called):
+ called.append('s1func')
+ called.append(node)
+ return []
+ def s2func(node, env, path, called=called):
+ called.append('s2func')
+ called.append(node)
+ return []
+ s1 = SCons.Scanner.Base(s1func)
+ s2 = SCons.Scanner.Base(s2func)
+ selector = SCons.Scanner.Selector({'.x' : s1, '.y' : s2})
+ nx = self.skey('.x')
+ selector(nx, None, [])
+ assert called == ['s1func', nx], called
+ del called[:]
+ ny = self.skey('.y')
+ selector(ny, None, [])
+ assert called == ['s2func', ny], called
+
+ def test_select(self):
+ """Test the Scanner.Selector select() method"""
+ selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey('.x'))
+ assert s == 1, s
+ s = selector.select(self.skey('.y'))
+ assert s == 2, s
+ s = selector.select(self.skey('.z'))
+ assert s is None, s
+
+ def test_add_scanner(self):
+ """Test the Scanner.Selector add_scanner() method"""
+ selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey('.z'))
+ assert s is None, s
+ selector.add_scanner('.z', 3)
+ s = selector.select(self.skey('.z'))
+ assert s == 3, s
+
class CurrentTestCase(unittest.TestCase):
def test_class(self):
"""Test the Scanner.Current class"""
@@ -355,6 +426,8 @@ def suite():
tclasses = [
FindPathDirsTestCase,
ScannerTestCase,
+ BaseTestCase,
+ SelectorTestCase,
CurrentTestCase,
ClassicTestCase,
ClassicCPPTestCase,
diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py
index 8f6c9e6..8432638 100644
--- a/src/engine/SCons/Scanner/__init__.py
+++ b/src/engine/SCons/Scanner/__init__.py
@@ -43,6 +43,15 @@ class _Null:
# used as an actual argument value.
_null = _Null
+def Scanner(function, *args, **kw):
+ """Public interface factory function for creating different types
+ of Scanners based on the different types of "functions" that may
+ be supplied."""
+ if SCons.Util.is_Dict(function):
+ return apply(Selector, (function,) + args, kw)
+ else:
+ return apply(Base, (function,) + args, kw)
+
class FindPathDirs:
"""A class to bind a specific *PATH variable name and the fs object
to a function that will return all of the *path directories."""
@@ -190,6 +199,31 @@ class Base:
return env.subst_list(self.skeys)[0]
return self.skeys
+ def select(self, node):
+ return self
+
+
+class Selector(Base):
+ """
+ A class for selecting a more specific scanner based on the
+ scanner_key() (suffix) for a specific Node.
+ """
+ def __init__(self, dict, *args, **kw):
+ Base.__init__(self, (None,)+args, kw)
+ self.dict = dict
+
+ def __call__(self, node, env, path = ()):
+ return self.select(node)(node, env, path)
+
+ def select(self, node):
+ try:
+ return self.dict[node.scanner_key()]
+ except KeyError:
+ return None
+
+ def add_scanner(self, skey, scanner):
+ self.dict[skey] = scanner
+
class Current(Base):
"""
@@ -200,6 +234,7 @@ class Current(Base):
def __init__(self, *args, **kw):
def current_check(node, env):
+ calc = env.get_calculator()
c = not node.has_builder() or node.current(env.get_calculator())
return c
kw['scan_check'] = current_check
diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py
index e5b3548..a253ddf 100644
--- a/src/engine/SCons/Taskmaster.py
+++ b/src/engine/SCons/Taskmaster.py
@@ -158,12 +158,10 @@ class Task:
their dependent parent nodes.
"""
for t in self.targets:
- def get_parents(node, parent): return node.get_parents()
- def set_state(node, parent): node.set_state(SCons.Node.failed)
- walker = SCons.Node.Walker(t, get_parents, eval_func=set_state)
- n = walker.next()
- while n:
- n = walker.next()
+ # Set failure state on all of the parents that were dependent
+ # on this failed build.
+ def set_state(node): node.set_state(SCons.Node.failed)
+ t.call_for_all_waiting_parents(set_state)
self.tm.executed(self.node)
@@ -340,6 +338,12 @@ class Taskmaster:
def unbuilt_nodes(node): return node.get_state() == None
not_built = filter(unbuilt_nodes, derived)
if not_built:
+ # We're waiting on one more derived files that have not
+ # yet been built. Add this node to the waiting_parents
+ # list of each of those derived files.
+ def add_to_waiting_parents(child, parent=node):
+ child.add_to_waiting_parents(parent)
+ map(add_to_waiting_parents, not_built)
not_built.reverse()
self.candidates.extend(self.order(not_built))
continue
diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py
index 8ca7e86..7010edb 100644
--- a/src/engine/SCons/TaskmasterTests.py
+++ b/src/engine/SCons/TaskmasterTests.py
@@ -50,15 +50,12 @@ class Node:
self.csig = None
self.state = None
self.prepared = None
- self.parents = []
+ self.waiting_parents = []
self.side_effect = 0
self.side_effects = []
self.alttargets = []
self.postprocessed = None
- for kid in kids:
- kid.parents.append(self)
-
def retrieve_from_cache(self):
global cache_text
if self.cached:
@@ -99,15 +96,18 @@ class Node:
global scan_called
scan_called = scan_called + 1
self.kids = self.kids + self.scans
- for scan in self.scans:
- scan.parents.append(self)
self.scans = []
def scanner_key(self):
return self.name
- def get_parents(self):
- return self.parents
+ def add_to_waiting_parents(self, node):
+ self.waiting_parents.append(node)
+
+ def call_for_all_waiting_parents(self, func):
+ func(self)
+ for parent in self.waiting_parents:
+ parent.call_for_all_waiting_parents(func)
def get_state(self):
return self.state
@@ -569,7 +569,6 @@ class TaskmasterTestCase(unittest.TestCase):
n2 = Node("n2", [n1])
n3 = Node("n3", [n2])
n1.kids = [n3]
- n3.parents.append(n1)
try:
tm = SCons.Taskmaster.Taskmaster([n3])
diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py
index 667e085..4364cf3 100644
--- a/src/engine/SCons/Tool/__init__.py
+++ b/src/engine/SCons/Tool/__init__.py
@@ -104,7 +104,7 @@ def createProgBuilder(env):
suffix = '$PROGSUFFIX',
src_suffix = '$OBJSUFFIX',
src_builder = 'Object',
- scanner = SCons.Defaults.ProgScan)
+ target_scanner = SCons.Defaults.ProgScan)
env['BUILDERS']['Program'] = program
return program
@@ -146,7 +146,7 @@ def createSharedLibBuilder(env):
emitter = "$SHLIBEMITTER",
prefix = '$SHLIBPREFIX',
suffix = '$SHLIBSUFFIX',
- scanner = SCons.Defaults.ProgScan,
+ target_scanner = SCons.Defaults.ProgScan,
src_suffix = '$SHOBJSUFFIX',
src_builder = 'SharedObject')
env['BUILDERS']['SharedLibrary'] = shared_lib
@@ -173,7 +173,8 @@ def createObjBuilders(env):
emitter = "$OBJEMITTER",
prefix = '$OBJPREFIX',
suffix = '$OBJSUFFIX',
- src_builder = ['CFile', 'CXXFile'])
+ src_builder = ['CFile', 'CXXFile'],
+ source_scanner = SCons.Defaults.ObjSourceScan)
env['BUILDERS']['StaticObject'] = static_obj
env['BUILDERS']['Object'] = static_obj
env['OBJEMITTER'] = SCons.Defaults.StaticObjectEmitter
@@ -185,7 +186,8 @@ def createObjBuilders(env):
emitter = "$SHOBJEMITTER",
prefix = '$SHOBJPREFIX',
suffix = '$SHOBJSUFFIX',
- src_builder = ['CFile', 'CXXFile'])
+ src_builder = ['CFile', 'CXXFile'],
+ source_scanner = SCons.Defaults.ObjSourceScan)
env['BUILDERS']['SharedObject'] = shared_obj
env['SHOBJEMITTER'] = SCons.Defaults.SharedObjectEmitter
diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py
index 1369a8b..22df3d4 100644
--- a/src/engine/SCons/Tool/mingw.py
+++ b/src/engine/SCons/Tool/mingw.py
@@ -137,7 +137,7 @@ def generate(env):
env['RCINCPREFIX'] = '--include-dir '
env['RCINCSUFFIX'] = ''
env['RCCOM'] = '$RC $RCINCFLAGS $RCFLAGS -i $SOURCE -o $TARGET'
- env.Append(CPPSUFFIXES = ['.rc'])
+ SCons.Defaults.ObjSourceScan.add_scanner('.rc', SCons.Defaults.CScan)
env['BUILDERS']['RES'] = res_builder
# Some setting from the platform also have to be overridden:
diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py
index 3d49802..17da702 100644
--- a/src/engine/SCons/Tool/msvc.py
+++ b/src/engine/SCons/Tool/msvc.py
@@ -448,7 +448,7 @@ def generate(env):
env['RC'] = 'rc'
env['RCFLAGS'] = SCons.Util.CLVar('')
env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
- env.Append(CPPSUFFIXES = ['.rc'])
+ SCons.Defaults.ObjSourceScan.add_scanner('.rc', SCons.Defaults.CScan)
env['BUILDERS']['RES'] = res_builder
try:
diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py
index f8234ba..a0dfed8 100644
--- a/src/engine/SCons/Tool/qt.py
+++ b/src/engine/SCons/Tool/qt.py
@@ -116,7 +116,7 @@ class _Automoc:
out_sources.append(moc_o)
objBuilder(moc_o, moc_cpp)
self.mocFromHBld(env, moc_cpp, h)
- moc_cpp.target_scanner = SCons.Defaults.CScan
+ #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)
@@ -126,7 +126,7 @@ class _Automoc:
env['QT_MOCNAMEGENERATOR'](base, src_ext, env)))
self.mocFromCppBld(env, moc, cpp)
env.Ignore(moc, moc)
- moc.source_scanner = SCons.Defaults.CScan
+ #moc.source_scanner = SCons.Defaults.CScan
os.chdir(old_os_cwd)
FS.chdir(old_fs_cwd)