summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-01-23 21:54:05 (GMT)
committerSteven Knight <knight@baldmt.com>2002-01-23 21:54:05 (GMT)
commitcb0829ad927a55c4d79125e6ced5e975544a05a6 (patch)
treec29ac37bb286f200da768ac48fb0d5845ec510d6
parent627e41f923a7db85b0d30ae1c6980c9fab2c642f (diff)
downloadSCons-cb0829ad927a55c4d79125e6ced5e975544a05a6.zip
SCons-cb0829ad927a55c4d79125e6ced5e975544a05a6.tar.gz
SCons-cb0829ad927a55c4d79125e6ced5e975544a05a6.tar.bz2
Strip $(-$) bracketed text from command lines.
-rw-r--r--doc/man/scons.142
-rw-r--r--src/CHANGES.txt4
-rw-r--r--src/engine/SCons/Action.py26
-rw-r--r--src/engine/SCons/ActionTests.py11
-rw-r--r--src/engine/SCons/Builder.py5
-rw-r--r--src/engine/SCons/BuilderTests.py12
-rw-r--r--src/engine/SCons/Node/FS.py4
-rw-r--r--src/engine/SCons/Node/__init__.py5
-rw-r--r--src/engine/SCons/Util.py13
-rw-r--r--src/engine/SCons/UtilTests.py34
-rw-r--r--test/CPPPATH.py15
-rw-r--r--test/LIBPATH.py12
12 files changed, 163 insertions, 20 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index ca03f60..dfb96c7 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -1255,6 +1255,48 @@ ${TARGET.filebase} => file
${TARGET.suffix} => .x
.EE
+.LP
+The special pseudo-variables
+.R $(
+and
+.R $)
+may be used to surround parts of a command line
+that may change
+.I without
+causing a rebuild--that is,
+which are not included in the signature
+of target files built with this command.
+All text between
+.R $(
+and
+.R $)
+will be removed from the command line
+before it is added to file signatures,
+and the
+.R $(
+and
+.R $)
+will be removed before the command is executed.
+For example, the command line:
+
+.ES
+echo Last build occurred $( $TODAY $). > $TARGET
+.EE
+
+.LP
+would execute the command:
+
+.ES
+echo Last build occurred $TODAY. > $TARGET
+.EE
+
+.LP
+but the command signature added to any target files would be:
+
+.ES
+echo Last build occurred . > $TARGET
+.EE
+
.\" XXX document how to add user defined scanners.
.SH EXAMPLES
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 462e06a..44946a0 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -38,6 +38,10 @@ RELEASE 0.04 -
- The C Scanner now always returns a sorted list of dependencies
so order changes don't cause unnecessary rebuilds.
+ - Strip $(-$) bracketed text from command lines. Use this to
+ surround $_INCDIRS and $_LIBDIRS so we don't rebuild in response
+ to changes to -I or -L options.
+
From Steve Leblanc:
- Add var=value command-line arguments.
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 38a4c70..26e81a5 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -31,6 +31,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import os.path
+import re
import string
import sys
@@ -238,6 +239,9 @@ class ActionBase:
return dict
+_rm = re.compile(r'\$[()]')
+_remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
+
class CommandAction(ActionBase):
"""Class for command-execution actions."""
def __init__(self, string):
@@ -246,7 +250,7 @@ class CommandAction(ActionBase):
def execute(self, **kw):
import SCons.Util
dict = apply(self.subst_dict, (), kw)
- cmd_list = SCons.Util.scons_subst_list(self.command, dict, {})
+ cmd_list = SCons.Util.scons_subst_list(self.command, dict, {}, _rm)
for cmd_line in cmd_list:
if len(cmd_line):
if print_actions:
@@ -262,8 +266,8 @@ class CommandAction(ActionBase):
return ret
return 0
- def get_contents(self, **kw):
- """Return the signature contents of this action's command line.
+ def _sig_dict(self, kw):
+ """Supply a dictionary for use in computing signatures.
For signature purposes, it doesn't matter what targets or
sources we use, so long as we use the same ones every time
@@ -272,8 +276,20 @@ class CommandAction(ActionBase):
"""
kw['target'] = ['__t1__', '__t2__']
kw['source'] = ['__s1__', '__s2__']
- dict = apply(self.subst_dict, (), kw)
- return SCons.Util.scons_subst(self.command, dict, {})
+ return apply(self.subst_dict, (), kw)
+
+ def get_raw_contents(self, **kw):
+ """Return the complete contents of this action's command line.
+ """
+ return SCons.Util.scons_subst(self.command, self._sig_dict(kw), {})
+
+ def get_contents(self, **kw):
+ """Return the signature contents of this action's command line.
+
+ This strips $(-$) and everything in between the string,
+ since those parts don't affect signatures.
+ """
+ return SCons.Util.scons_subst(self.command, self._sig_dict(kw), {}, _remove)
class FunctionAction(ActionBase):
"""Class for Python function actions."""
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index a70fd99..30bf093 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -125,12 +125,19 @@ class CommandActionTestCase(unittest.TestCase):
a.execute()
assert t.executed == 1
+ def test_get_raw_contents(self):
+ """Test fetching the contents of a command Action
+ """
+ a = SCons.Action.CommandAction("| $( $foo | $bar $) |")
+ c = a.get_contents(foo = 'FFF', bar = 'BBB')
+ assert c == "| $( FFF | BBB $) |"
+
def test_get_contents(self):
"""Test fetching the contents of a command Action
"""
- a = SCons.Action.CommandAction("| $foo | $bar |")
+ a = SCons.Action.CommandAction("| $foo $( | $) $bar |")
c = a.get_contents(foo = 'FFF', bar = 'BBB')
- assert c == "| FFF | BBB |"
+ assert c == "| FFF BBB |"
class FunctionActionTestCase(unittest.TestCase):
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index db1e701..6fffeee 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -145,6 +145,11 @@ class BuilderBase:
"""
return apply(self.action.execute, (), kw)
+ def get_raw_contents(self, **kw):
+ """Fetch the "contents" of the builder's action.
+ """
+ return apply(self.action.get_raw_contents, (), kw)
+
def get_contents(self, **kw):
"""Fetch the "contents" of the builder's action
(for signature calculation).
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index e383ee1..65aa934 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -349,8 +349,11 @@ class BuilderTestCase(unittest.TestCase):
'INCPREFIX' : '-I',
'INCSUFFIX' : ''}
+ contents = apply(b4.get_raw_contents, (), kw)
+ assert contents == "-ll1 -ll2 $( -LlibX $) $( -Ic -Ip $)", contents
+
contents = apply(b4.get_contents, (), kw)
- assert contents == "-ll1 -ll2 -LlibX -Ic -Ip", contents
+ assert contents == "-ll1 -ll2", "'%s'" % contents
# SCons.Node.FS has been imported by our import of
# SCons.Node.Builder. It's kind of bogus that we don't
@@ -358,8 +361,13 @@ class BuilderTestCase(unittest.TestCase):
# maybe a little cleaner than tying these tests directly
# to the other module via a direct import.
kw['dir'] = SCons.Node.FS.default_fs.Dir('d')
+
+ contents = apply(b4.get_raw_contents, (), kw)
+ expect = os.path.normpath("-ll1 -ll2 $( -Ld/libX $) $( -Id/c -Id/p $)")
+ assert contents == expect, contents + " != " + expect
+
contents = apply(b4.get_contents, (), kw)
- expect = os.path.normpath("-ll1 -ll2 -Ld/libX -Id/c -Id/p")
+ expect = os.path.normpath("-ll1 -ll2")
assert contents == expect, contents + " != " + expect
def test_node_factory(self):
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 66b7314..96ce148 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -162,6 +162,7 @@ class FS:
except KeyError:
dir_temp = Dir(path_name, directory)
directory.entries[path_norm] = dir_temp
+ directory.add_wkid(dir_temp)
directory = dir_temp
file_name = _my_normcase(path_comp[-1])
try:
@@ -169,6 +170,7 @@ class FS:
except KeyError:
ret = fsclass(path_comp[-1], directory)
directory.entries[file_name] = ret
+ directory.add_wkid(ret)
return ret
def __transformPath(self, name, directory):
@@ -431,7 +433,7 @@ class Dir(Entry):
if s and (not state or s > state):
state = s
import SCons.Node
- if state == SCons.Node.up_to_date:
+ if state == 0 or state == SCons.Node.up_to_date:
return 1
else:
return 0
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index b11027f..f2d379f 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -190,6 +190,11 @@ class Node:
if parent not in self.parents: self.parents.append(parent)
+ 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(self):
#XXX Need to remove duplicates from this
return self.sources \
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index a207ffc..16d2c76 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -170,7 +170,7 @@ class PathList(UserList.UserList):
_cv = re.compile(r'\$([_a-zA-Z]\w*|{[^}]*})')
_space_sep = re.compile(r'[\t ]+(?![^{]*})')
-def scons_subst_list(strSubst, locals, globals):
+def scons_subst_list(strSubst, locals, globals, remove=None):
"""
This function is similar to scons_subst(), but with
one important difference. Instead of returning a single
@@ -214,10 +214,12 @@ def scons_subst_list(strSubst, locals, globals):
strSubst, n = _cv.subn(repl, strSubst)
# Now parse the whole list into tokens.
listLines = string.split(strSubst, '\n')
+ if remove:
+ listLines = map(lambda x,re=remove: re.sub('', x), listLines)
return map(lambda x: filter(lambda y: y, string.split(x, '\0')),
listLines)
-def scons_subst(strSubst, locals, globals):
+def scons_subst(strSubst, locals, globals, remove=None):
"""Recursively interpolates dictionary variables into
the specified string, returning the expanded result.
Variables are specified by a $ prefix in the string and
@@ -227,7 +229,7 @@ def scons_subst(strSubst, locals, globals):
surrounded by curly braces to separate the name from
trailing characters.
"""
- cmd_list = scons_subst_list(strSubst, locals, globals)
+ cmd_list = scons_subst_list(strSubst, locals, globals, remove)
return string.join(map(string.join, cmd_list), '\n')
def find_files(filenames, paths,
@@ -351,6 +353,11 @@ class DirVarInterp(VarInterpolator):
self.dictInstCache[(dir, fs)] = ret
return ret
+ def generate(self, dict):
+ VarInterpolator.generate(self, dict)
+ if dict[self.dest]:
+ dict[self.dest] = ['$('] + dict[self.dest] + ['$)']
+
AUTO_GEN_VARS = ( VarInterpolator('_LIBFLAGS',
'LIBS',
'LIBLINKPREFIX',
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index e9e763c..ed72ebb 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -25,6 +25,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import os.path
+import re
import string
import sys
import unittest
@@ -127,6 +128,21 @@ class UtilTestCase(unittest.TestCase):
newcom = scons_subst("test $xxx", loc, {})
assert newcom == cvt("test"), newcom
+ newcom = scons_subst("test $($xxx$)", loc, {})
+ assert newcom == cvt("test $($)"), newcom
+
+ newcom = scons_subst("test $( $xxx $)", loc, {})
+ assert newcom == cvt("test $( $)"), newcom
+
+ newcom = scons_subst("test $($xxx$)", loc, {}, re.compile('\$[()]'))
+ assert newcom == cvt("test"), newcom
+
+ newcom = scons_subst("test $( $xxx $)", loc, {}, re.compile('\$[()]'))
+ assert newcom == cvt("test"), newcom
+
+ newcom = scons_subst("test aXbXcXd", loc, {}, re.compile('X'))
+ assert newcom == cvt("test abcd"), newcom
+
def test_subst_list(self):
"""Testing the scons_subst_list() method..."""
loc = {}
@@ -196,18 +212,22 @@ class UtilTestCase(unittest.TestCase):
'INCSUFFIX' : 'bar',
'FOO' : 'baz' }
autogenerate(dict, dir = SCons.Node.FS.default_fs.Dir('/xx'))
- assert len(dict['_INCFLAGS']) == 5, dict['_INCFLAGS']
- assert dict['_INCFLAGS'][0] == os.path.normpath('foo/xx/foobar'), \
+ assert len(dict['_INCFLAGS']) == 7, dict['_INCFLAGS']
+ assert dict['_INCFLAGS'][0] == '$(', \
dict['_INCFLAGS'][0]
- assert dict['_INCFLAGS'][1] == os.path.normpath('foo/xx/barbar'), \
+ assert dict['_INCFLAGS'][1] == os.path.normpath('foo/xx/foobar'), \
dict['_INCFLAGS'][1]
- assert dict['_INCFLAGS'][2] == os.path.normpath('foo/xx/bazbar'), \
+ assert dict['_INCFLAGS'][2] == os.path.normpath('foo/xx/barbar'), \
dict['_INCFLAGS'][2]
- assert dict['_INCFLAGS'][3] == os.path.normpath('foo/xx/baz/barbar'), \
+ assert dict['_INCFLAGS'][3] == os.path.normpath('foo/xx/bazbar'), \
dict['_INCFLAGS'][3]
-
- assert dict['_INCFLAGS'][4] == os.path.normpath('fooblatbar'), \
+ assert dict['_INCFLAGS'][4] == os.path.normpath('foo/xx/baz/barbar'), \
dict['_INCFLAGS'][4]
+
+ assert dict['_INCFLAGS'][5] == os.path.normpath('fooblatbar'), \
+ dict['_INCFLAGS'][5]
+ assert dict['_INCFLAGS'][6] == '$)', \
+ dict['_INCFLAGS'][6]
def test_render_tree(self):
class Node:
diff --git a/test/CPPPATH.py b/test/CPPPATH.py
index af7bdd8..30439ad 100644
--- a/test/CPPPATH.py
+++ b/test/CPPPATH.py
@@ -161,4 +161,19 @@ test.fail_test(os.path.exists(test.workpath('variant', 'prog.c')))
test.up_to_date(arguments = args)
+# Change CPPPATH and make sure we don't rebuild because of it.
+test.write('SConstruct', """
+env = Environment(CPPPATH = ['include'])
+obj = env.Object(target='prog', source='subdir/prog.c')
+env.Program(target='prog', source=obj)
+SConscript('subdir/SConscript', "env")
+
+BuildDir('variant', 'subdir', 0)
+include = Dir('include')
+env = Environment(CPPPATH=[include])
+SConscript('variant/SConscript', "env")
+""")
+
+test.up_to_date(arguments = args)
+
test.pass_test()
diff --git a/test/LIBPATH.py b/test/LIBPATH.py
index 7d5a7cc..a373425 100644
--- a/test/LIBPATH.py
+++ b/test/LIBPATH.py
@@ -60,4 +60,16 @@ test.run(arguments = '.')
test.run(program = test.workpath('prog'),
stdout = "f1.c\nprog.c\n")
+test.up_to_date(arguments = '.')
+
+# Change LIBPATH and make sure we don't rebuild because of it.
+test.write('SConstruct', """
+env = Environment(LIBS = [ 'foo1' ],
+ LIBPATH = [ './libs', './lib2' ])
+env.Program(target = 'prog', source = 'prog.c')
+env.Library(target = './libs/foo1', source = 'f1.c')
+""")
+
+test.up_to_date(arguments = '.', stderr = None)
+
test.pass_test()