${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
- 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.
import os
import os.path
+import re
import string
import sys
return dict
+_rm = re.compile(r'\$[()]')
+_remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
+
class CommandAction(ActionBase):
"""Class for command-execution actions."""
def __init__(self, string):
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:
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
"""
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."""
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):
"""
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).
'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
# 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):
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:
except KeyError:
ret = fsclass(path_comp[-1], directory)
directory.entries[file_name] = ret
+ directory.add_wkid(ret)
return ret
def __transformPath(self, name, directory):
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
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 \
_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
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
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,
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',
import os
import os.path
+import re
import string
import sys
import unittest
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 = {}
'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:
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()
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()