summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-01-06 03:54:25 (GMT)
committerSteven Knight <knight@baldmt.com>2003-01-06 03:54:25 (GMT)
commit6702e9dce5182eaa012da9dc511fcf85cf205049 (patch)
tree08fd77ca8579ba8426241b560202cd8f9a20c3f8
parent51974ccdc1ee8340c54f8fa02670c554d20ef157 (diff)
downloadSCons-6702e9dce5182eaa012da9dc511fcf85cf205049.zip
SCons-6702e9dce5182eaa012da9dc511fcf85cf205049.tar.gz
SCons-6702e9dce5182eaa012da9dc511fcf85cf205049.tar.bz2
Performance improvement: cut down on Proxy.__getattr__ calls.
-rw-r--r--src/CHANGES.txt3
-rw-r--r--src/engine/SCons/Builder.py2
-rw-r--r--src/engine/SCons/BuilderTests.py2
-rw-r--r--src/engine/SCons/Environment.py6
-rw-r--r--src/engine/SCons/Node/FS.py16
-rw-r--r--src/engine/SCons/Node/__init__.py19
-rw-r--r--src/engine/SCons/Script/__init__.py12
-rw-r--r--src/engine/SCons/Sig/SigTests.py13
-rw-r--r--src/engine/SCons/Sig/__init__.py4
-rw-r--r--src/engine/SCons/Taskmaster.py2
-rw-r--r--src/engine/SCons/TaskmasterTests.py3
11 files changed, 54 insertions, 28 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 7cb4eb9..88ee9e8 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -31,6 +31,9 @@ RELEASE 0.10 - XXX
- Fix SCons not exiting with the appropriate status on build errors
(and probably in other situations).
+ - Significant performance improvement from using a more efficient
+ check, throughout the code, for whether a Node has a Builder.
+
From Steve Leblanc:
- Add a Clean() method to support removing user-specified targets
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 2f5df7f..15358c1 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -149,7 +149,7 @@ def _init_nodes(builder, env, overrides, tlist, slist):
for t in tlist:
if t.side_effect:
raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
- if t.builder is not None:
+ if t.has_builder():
if t.env != env:
raise UserError, "Two different environments were specified for the same target: %s"%str(t)
elif t.overrides != overrides:
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 5906314..9905eb9 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -116,6 +116,8 @@ class BuilderTestCase(unittest.TestCase):
return self.name
def builder_set(self, builder):
self.builder = builder
+ def has_builder(self):
+ return not self.builder is None
def env_set(self, env, safe=0):
self.env = env
def add_source(self, source):
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 8861a99..a5672ae 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -360,9 +360,9 @@ class Environment:
for side_effect in side_effects:
# A builder of 1 means the node is supposed to appear
- # buildable without actually having a builder, so we allow
- # it to be a side effect as well.
- if side_effect.builder is not None and side_effect.builder != 1:
+ # buildable without actually having a builder, so we allow
+ # it to be a side effect as well.
+ if side_effect.has_builder() and side_effect.builder != 1:
raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect)
side_effect.add_source(targets)
side_effect.side_effect = 1
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 0569dea..826307b 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -182,7 +182,7 @@ class Entry(SCons.Node.Node):
def __str__(self):
"""A FS node's string representation is its path name."""
- if self.duplicate or self.builder:
+ if self.duplicate or self.has_builder():
return self.path
return self.srcnode().path
@@ -513,7 +513,7 @@ class FS:
# This is usually the case with BuildDir().
# We only want to find pre-existing files.
if rnode.exists() and \
- (isinstance(rnode, Dir) or not rnode.builder):
+ (isinstance(rnode, Dir) or not rnode.has_builder()):
return rnode
except TypeError:
pass # Wrong type of node.
@@ -558,7 +558,7 @@ class FS:
# want it to show up in the build tree. This is usually the
# case with BuildDir(). We only want to find pre-existing files.
if (not must_exist or rnode.exists()) and \
- (not rnode.builder or isinstance(rnode, Dir)):
+ (not rnode.has_builder() or isinstance(rnode, Dir)):
ret.append(rnode)
except TypeError:
pass # Wrong type of node.
@@ -819,7 +819,7 @@ class File(Entry):
in the .sconsign file.
"""
- if self.builder:
+ if self.has_builder():
if SCons.Sig.build_signature:
if not hasattr(self, 'bsig'):
self.set_bsig(calc.bsig(self.rfile()))
@@ -904,14 +904,14 @@ class File(Entry):
"""Prepare for this file to be created."""
def missing(node):
- return not node.builder and not node.linked and not node.rexists()
+ return not node.has_builder() and not node.linked and not node.rexists()
missing_sources = filter(missing, self.children())
if missing_sources:
desc = "No Builder for target `%s', needed by `%s'." % (missing_sources[0], self)
raise SCons.Errors.StopError, desc
if self.exists():
- if self.builder and not self.precious:
+ if self.has_builder() and not self.precious:
Unlink(self, None, None)
if hasattr(self, '_exists'):
delattr(self, '_exists')
@@ -931,7 +931,7 @@ class File(Entry):
def exists(self):
# Duplicate from source path if we are set up to do this.
- if self.duplicate and not self.builder and not self.linked:
+ if self.duplicate and not self.has_builder() and not self.linked:
src=self.srcnode().rfile()
if src.exists() and src.abspath != self.abspath:
self._createDir()
@@ -1008,7 +1008,7 @@ def find_file(filename, paths, node_factory = default_fs.File):
try:
node = node_factory(filename, dir)
# Return true of the node exists or is a derived node.
- if node.builder or \
+ if node.has_builder() or \
(isinstance(node, SCons.Node.FS.Entry) and node.exists()):
retval = node
break
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index e5aae5c..3bafb9c 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -114,7 +114,7 @@ class Node:
so only do thread safe stuff here. Do thread unsafe stuff in
built().
"""
- if not self.builder:
+ if not self.has_builder():
return None
action_list = self.builder.get_actions()
if not action_list:
@@ -160,6 +160,19 @@ class Node:
def builder_set(self, builder):
self.builder = builder
+ def has_builder(self):
+ """Return whether this Node has a builder or not.
+
+ In Boolean tests, this turns out to be a *lot* more efficient
+ than simply examining the builder attribute directly ("if
+ node.builder: ..."). When the builder attribute is examined
+ directly, it ends up calling __getattr__ for both the __len__
+ and __nonzero__ attributes on instances of our Builder Proxy
+ class(es), generating a bazillion extra calls and slowing
+ things down immensely.
+ """
+ return not self.builder is None
+
def builder_sig_adapter(self):
"""Create an adapter for calculating a builder's signature.
@@ -190,7 +203,7 @@ class Node:
if not self.implicit is None:
return
self.implicit = []
- if not self.builder:
+ if not self.has_builder():
return
if implicit_cache and not implicit_deps_changed:
@@ -243,7 +256,7 @@ class Node:
in the .sconsign file.
"""
- if self.builder:
+ if self.has_builder():
if SCons.Sig.build_signature:
if not hasattr(self, 'bsig'):
self.set_bsig(calc.bsig(self))
diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py
index 93f4795..ca6536f 100644
--- a/src/engine/SCons/Script/__init__.py
+++ b/src/engine/SCons/Script/__init__.py
@@ -75,9 +75,9 @@ class BuildTask(SCons.Taskmaster.Task):
def execute(self):
target = self.targets[0]
if target.get_state() == SCons.Node.up_to_date:
- if self.top and target.builder:
+ if self.top and target.has_builder():
display('scons: "%s" is up to date.' % str(target))
- elif target.builder and not hasattr(target.builder, 'status'):
+ elif target.has_builder() and not hasattr(target.builder, 'status'):
if print_time:
start_time = time.time()
SCons.Taskmaster.Task.execute(self)
@@ -100,7 +100,7 @@ class BuildTask(SCons.Taskmaster.Task):
def executed(self):
t = self.targets[0]
- if self.top and not t.builder and not t.side_effect:
+ if self.top and not t.has_builder() and not t.side_effect:
if not t.exists():
sys.stderr.write("scons: *** Do not know how to make target `%s'." % t)
if not keep_going_on_error:
@@ -145,7 +145,7 @@ class BuildTask(SCons.Taskmaster.Task):
class CleanTask(SCons.Taskmaster.Task):
"""An SCons clean task."""
def show(self):
- if self.targets[0].builder or self.targets[0].side_effect:
+ if self.targets[0].has_builder() or self.targets[0].side_effect:
display("Removed " + str(self.targets[0]))
if SCons.Script.SConscript.clean_targets.has_key(str(self.targets[0])):
files = SCons.Script.SConscript.clean_targets[str(self.targets[0])]
@@ -153,7 +153,7 @@ class CleanTask(SCons.Taskmaster.Task):
SCons.Utils.fs_delete(str(f), 0)
def remove(self):
- if self.targets[0].builder or self.targets[0].side_effect:
+ if self.targets[0].has_builder() or self.targets[0].side_effect:
for t in self.targets:
try:
removed = t.remove()
@@ -217,7 +217,7 @@ def get_all_children(node): return node.all_children(None)
def get_derived_children(node):
children = node.all_children(None)
- return filter(lambda x: x.builder, children)
+ return filter(lambda x: x.has_builder(), children)
def _scons_syntax_error(e):
"""Handle syntax errors. Print out a message and show where the error
diff --git a/src/engine/SCons/Sig/SigTests.py b/src/engine/SCons/Sig/SigTests.py
index ccd5897..57c5a51 100644
--- a/src/engine/SCons/Sig/SigTests.py
+++ b/src/engine/SCons/Sig/SigTests.py
@@ -59,6 +59,9 @@ class DummyNode:
self.oldbsig = 0
self.oldcsig = 0
+ def has_builder(self):
+ return self.builder
+
def get_contents(self):
# a file that doesn't exist has no contents:
assert self.exists()
@@ -97,7 +100,7 @@ class DummyNode:
return None
def calc_signature(self, calc):
- if self.builder:
+ if self.has_builder():
return calc.bsig(self)
else:
return calc.csig(self)
@@ -212,7 +215,7 @@ class SigTestBase:
for node in nodes:
self.failUnless(not current(calc, node),
- "none of the nodes should be current")
+ "node %s should not be current" % node.path)
# simulate a build:
self.files[1].modify('built', 222)
@@ -230,7 +233,7 @@ class SigTestBase:
for node in nodes:
self.failUnless(current(calc, node),
- "all of the nodes should be current")
+ "node %s should be current" % node.path)
def test_modify(self):
@@ -278,7 +281,7 @@ class SigTestBase:
for node in nodes:
self.failUnless(current(calc, node),
- "all of the nodes should be current")
+ "node %s should be current" % node.path)
class MD5TestCase(unittest.TestCase, SigTestBase):
@@ -311,6 +314,8 @@ class CalcTestCase(unittest.TestCase):
self.ignore = []
self.builder = None
self.use_signature = 1
+ def has_builder(self):
+ return not self.builder is None
def children(self):
return filter(lambda x, i=self.ignore: x not in i, self.kids)
def all_children(self):
diff --git a/src/engine/SCons/Sig/__init__.py b/src/engine/SCons/Sig/__init__.py
index 5de89fa..4fe9423 100644
--- a/src/engine/SCons/Sig/__init__.py
+++ b/src/engine/SCons/Sig/__init__.py
@@ -263,7 +263,7 @@ class Calculator:
what's wanted.
"""
sigs = map(lambda n, c=self: n.calc_signature(c), node.children())
- if node.builder:
+ if node.has_builder():
sigs.append(self.module.signature(node.builder_sig_adapter()))
bsig = self.module.collect(filter(lambda x: not x is None, sigs))
@@ -320,7 +320,7 @@ class Calculator:
"""
oldtime, oldbsig, oldcsig = node.get_prevsiginfo()
- if not node.builder and node.get_timestamp() == oldtime:
+ if not node.has_builder() and node.get_timestamp() == oldtime:
return 1
return self.module.current(newsig, oldbsig)
diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py
index eec50bf..74703f2 100644
--- a/src/engine/SCons/Taskmaster.py
+++ b/src/engine/SCons/Taskmaster.py
@@ -213,7 +213,7 @@ class Taskmaster:
# Add non-derived files that have not been built
# to the candidates list:
def derived(node):
- return (node.builder or node.side_effect) and node.get_state() == None
+ return (node.has_builder() or node.side_effect) and node.get_state() == None
derived = filter(derived, children)
if derived:
derived.reverse()
diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py
index d8bcc4f..79ff11b 100644
--- a/src/engine/SCons/TaskmasterTests.py
+++ b/src/engine/SCons/TaskmasterTests.py
@@ -57,6 +57,9 @@ class Node:
global built_text
built_text = self.name + " built"
+ def has_builder(self):
+ return not self.builder is None
+
def built(self):
global built_text
built_text = built_text + " really"