summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Oberbrunner <garyo@oberbrunner.com>2013-11-02 16:27:42 -0400
committerGary Oberbrunner <garyo@oberbrunner.com>2013-11-02 16:27:42 -0400
commitced06899b554ad01ab12794c9410b09cd3334305 (patch)
tree69c4681b26c8c9d2e94d7c7d24bbe764ec19e4ad
parent9869c87359b8e1a73c18f281ed8fe066d351a210 (diff)
parent966e4d5a394911456983d410a5a7a1ca120c646a (diff)
downloadscons-ced06899b554ad01ab12794c9410b09cd3334305.tar.gz
Merge pull request #88: various usability enhancements
* Allow multiple --debug= values * Add support for a readonly cache (--cache-readonly) * Always print stats if requested * Generally try harder to print out a message on build errors
-rw-r--r--doc/man/scons.xml8
-rwxr-xr-x[-rw-r--r--]runtest.py38
-rw-r--r--src/engine/SCons/Environment.py34
-rw-r--r--src/engine/SCons/EnvironmentTests.py31
-rw-r--r--src/engine/SCons/Node/NodeTests.py13
-rw-r--r--src/engine/SCons/Node/__init__.py16
-rw-r--r--src/engine/SCons/Script/Main.py1
-rw-r--r--src/engine/SCons/Script/Main.xml21
-rw-r--r--src/engine/SCons/Warnings.py3
-rw-r--r--test/Pseudo.py65
-rw-r--r--test/option/warn-dependency.py1
-rw-r--r--test/warning-TargetNotBuiltWarning.py55
12 files changed, 253 insertions, 33 deletions
diff --git a/doc/man/scons.xml b/doc/man/scons.xml
index 3f47b470..0109e225 100644
--- a/doc/man/scons.xml
+++ b/doc/man/scons.xml
@@ -1940,6 +1940,14 @@ These warnings are enabled by default.</para>
</listitem>
</varlistentry>
<varlistentry>
+ <term>--warn=target_not_build, --warn=no-target_not_built</term>
+ <listitem>
+<para>Enables or disables warnings about a build rule not building the
+ expected targets. These warnings are not currently enabled by default.</para>
+
+ </listitem>
+ </varlistentry>
+ <varlistentry>
<term>-Y<emphasis> repository</emphasis>, --repository=<emphasis>repository</emphasis>, --srcdir=<emphasis>repository</emphasis></term>
<listitem>
<para>Search the specified repository for any input and target
diff --git a/runtest.py b/runtest.py
index 6beb4ba7..2b7f7cee 100644..100755
--- a/runtest.py
+++ b/runtest.py
@@ -58,6 +58,8 @@
# command line it will execute before
# executing it. This suppresses that print.
#
+# --quit-on-failure Quit on any test failure
+#
# -s Short progress. Prints only the command line
# and a percentage value, based on the total and
# current number of tests.
@@ -123,6 +125,7 @@ print_progress = 1
suppress_stdout = False
suppress_stderr = False
allow_pipe_files = True
+quit_on_failure = False
helpstr = """\
Usage: runtest.py [OPTIONS] [TEST ...]
@@ -158,6 +161,7 @@ Options:
--passed Summarize which tests passed.
--qmtest Run using the QMTest harness (deprecated).
-q, --quiet Don't print the test being executed.
+ --quit-on-failure Quit on any test failure
-s, --short-progress Short progress, prints only the command line
and a percentage value, based on the total and
current number of tests.
@@ -214,7 +218,9 @@ opts, args = getopt.getopt(args, "3b:def:hj:klnP:p:qsv:Xx:t",
'jobs=',
'list', 'no-exec', 'nopipefiles',
'package=', 'passed', 'python=', 'qmtest',
- 'quiet', 'short-progress', 'time',
+ 'quiet',
+ 'quit-on-failure',
+ 'short-progress', 'time',
'version=', 'exec=',
'verbose='])
@@ -267,7 +273,9 @@ for o, a in opts:
elif o in ['-q', '--quiet']:
printcommand = 0
suppress_stdout = True
- suppress_stderr = True
+ suppress_stderr = True
+ elif o in ['--quit-on-failure']:
+ quit_on_failure = True
elif o in ['-s', '--short-progress']:
print_progress = 1
suppress_stdout = True
@@ -373,16 +381,16 @@ else:
# Else, we catch the output of both pipes...
if allow_pipe_files:
# The subprocess.Popen() suffers from a well-known
- # problem. Data for stdout/stderr is read into a
+ # problem. Data for stdout/stderr is read into a
# memory buffer of fixed size, 65K which is not very much.
# When it fills up, it simply stops letting the child process
# write to it. The child will then sit and patiently wait to
- # be able to write the rest of its output. Hang!
+ # be able to write the rest of its output. Hang!
# In order to work around this, we follow a suggestion
# by Anders Pearson in
# http://http://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/
# and pass temp file objects to Popen() instead of the ubiquitous
- # subprocess.PIPE.
+ # subprocess.PIPE.
def spawn_it(command_args):
# Create temporary files
import tempfile
@@ -395,7 +403,7 @@ else:
shell=True)
# ... and wait for it to finish.
ret = p.wait()
-
+
try:
# Rewind to start of files
tmp_stdout.seek(0)
@@ -407,10 +415,10 @@ else:
# Remove temp files by closing them
tmp_stdout.close()
tmp_stderr.close()
-
+
# Return values
return (spawned_stderr, spawned_stdout, ret)
-
+
else:
# We get here only if the user gave the '--nopipefiles'
# option, meaning the "temp file" approach for
@@ -419,9 +427,9 @@ else:
# potential deadlock situation in the following code:
# If the subprocess writes a lot of data to its stderr,
# the pipe will fill up (nobody's reading it yet) and the
- # subprocess will wait for someone to read it.
+ # subprocess will wait for someone to read it.
# But the parent process is trying to read from stdin
- # (but the subprocess isn't writing anything there).
+ # (but the subprocess isn't writing anything there).
# Hence a deadlock.
# Be dragons here! Better don't use this!
def spawn_it(command_args):
@@ -469,7 +477,7 @@ class PopenExecutor(Base):
shell=True)
# ... and wait for it to finish.
self.status = p.wait()
-
+
try:
# Rewind to start of files
tmp_stdout.seek(0)
@@ -481,7 +489,7 @@ class PopenExecutor(Base):
# Remove temp files by closing them
tmp_stdout.close()
tmp_stderr.close()
- else:
+ else:
def execute(self):
p = subprocess.Popen(self.command_str,
stdout=subprocess.PIPE,
@@ -832,7 +840,7 @@ def run_test(t, io_lock, async=True):
if head:
os.environ['PYTHON_SCRIPT_DIR'] = head
else:
- os.environ['PYTHON_SCRIPT_DIR'] = ''
+ os.environ['PYTHON_SCRIPT_DIR'] = ''
test_start_time = time_func()
if execute_tests:
t.execute()
@@ -849,6 +857,10 @@ def run_test(t, io_lock, async=True):
print_time_func("Test execution time: %.1f seconds\n", t.test_time)
if io_lock:
io_lock.release()
+ if quit_on_failure and t.status == 1:
+ print "Exiting due to error"
+ print t.status
+ sys.exit(1)
class RunTest(threading.Thread):
def __init__(self, queue, io_lock):
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index ca5df588..3e499b6c 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -705,7 +705,7 @@ class SubstitutionEnvironment(object):
# -symbolic (linker global binding)
# -R dir (deprecated linker rpath)
# IBM compilers may also accept -qframeworkdir=foo
-
+
params = shlex.split(arg)
append_next_arg_to = None # for multi-word args
for arg in params:
@@ -795,7 +795,7 @@ class SubstitutionEnvironment(object):
append_next_arg_to = arg
else:
dict['CCFLAGS'].append(arg)
-
+
for arg in flags:
do_parse(arg)
return dict
@@ -859,7 +859,7 @@ class SubstitutionEnvironment(object):
# def MergeShellPaths(self, args, prepend=1):
# """
-# Merge the dict in args into the shell environment in env['ENV'].
+# Merge the dict in args into the shell environment in env['ENV'].
# Shell path elements are appended or prepended according to prepend.
# Uses Pre/AppendENVPath, so it always appends or prepends uniquely.
@@ -962,14 +962,14 @@ class Base(SubstitutionEnvironment):
platform = SCons.Platform.Platform(platform)
self._dict['PLATFORM'] = str(platform)
platform(self)
-
+
self._dict['HOST_OS'] = self._dict.get('HOST_OS',None)
self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None)
-
+
# Now set defaults for TARGET_{OS|ARCH}
self._dict['TARGET_OS'] = self._dict.get('TARGET_OS',None)
self._dict['TARGET_ARCH'] = self._dict.get('TARGET_ARCH',None)
-
+
# Apply the passed-in and customizable variables to the
# environment before calling the tools, because they may use
@@ -1158,7 +1158,7 @@ class Base(SubstitutionEnvironment):
# "continue" statements whenever we finish processing an item,
# but Python 1.5.2 apparently doesn't let you use "continue"
# within try:-except: blocks, so we have to nest our code.
- try:
+ try:
if key == 'CPPDEFINES' and SCons.Util.is_String(self._dict[key]):
self._dict[key] = [self._dict[key]]
orig = self._dict[key]
@@ -1209,7 +1209,7 @@ class Base(SubstitutionEnvironment):
orig = orig.items()
orig += val
self._dict[key] = orig
- else:
+ else:
for v in val:
orig[v] = None
else:
@@ -1232,7 +1232,7 @@ class Base(SubstitutionEnvironment):
path = str(self.fs.Dir(path))
return path
- def AppendENVPath(self, name, newpath, envname = 'ENV',
+ def AppendENVPath(self, name, newpath, envname = 'ENV',
sep = os.pathsep, delete_existing=1):
"""Append path elements to the path 'name' in the 'ENV'
dictionary for this environment. Will only add any particular
@@ -1290,7 +1290,7 @@ class Base(SubstitutionEnvironment):
dk = dk.items()
elif SCons.Util.is_String(dk):
dk = [(dk,)]
- else:
+ else:
tmp = []
for i in dk:
if SCons.Util.is_List(i):
@@ -1335,7 +1335,7 @@ class Base(SubstitutionEnvironment):
dk = filter(lambda x, val=val: x not in val, dk)
self._dict[key] = dk + val
else:
- dk = [x for x in dk if x not in val]
+ dk = [x for x in dk if x not in val]
self._dict[key] = dk + val
else:
# By elimination, val is not a list. Since dk is a
@@ -1382,7 +1382,7 @@ class Base(SubstitutionEnvironment):
builders = self._dict['BUILDERS']
except KeyError:
pass
-
+
clone = copy.copy(self)
# BUILDERS is not safe to do a simple copy
clone._dict = semi_deepcopy_dict(self._dict, ['BUILDERS'])
@@ -1410,7 +1410,7 @@ class Base(SubstitutionEnvironment):
apply_tools(clone, tools, toolpath)
# apply them again in case the tools overwrote them
- clone.Replace(**new)
+ clone.Replace(**new)
# Finally, apply any flags to be merged in
if parse_flags: clone.MergeFlags(parse_flags)
@@ -2087,6 +2087,14 @@ class Base(SubstitutionEnvironment):
t.set_precious()
return tlist
+ def Pseudo(self, *targets):
+ tlist = []
+ for t in targets:
+ tlist.extend(self.arg2nodes(t, self.fs.Entry))
+ for t in tlist:
+ t.set_pseudo()
+ return tlist
+
def Repository(self, *dirs, **kw):
dirs = self.arg2nodes(list(dirs), self.fs.Dir)
self.fs.Repository(*dirs, **kw)
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index db788dc6..7fa8af4e 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -170,7 +170,7 @@ class TestEnvironmentFixture(object):
single_source = 1)
kw['BUILDERS'] = {'Object' : static_obj}
static_obj.add_action('.cpp', 'fake action')
-
+
env = Environment(*args, **kw)
return env
@@ -1722,7 +1722,7 @@ def exists(env):
assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end
env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1)
assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last
-
+
env['CLVar'] = CLVar([])
env.AppendUnique(CLVar = 'bar')
result = env['CLVar']
@@ -2030,7 +2030,7 @@ def generate(env):
try:
save_command = []
- env.backtick = my_backtick(save_command,
+ env.backtick = my_backtick(save_command,
"-I/usr/include/fum -I bar -X\n" + \
"-L/usr/fax -L foo -lxxx -l yyy " + \
"-Wa,-as -Wl,-link " + \
@@ -2375,7 +2375,7 @@ f5: \
env.PrependUnique(DDD1 = ['a','c'], delete_existing=1)
assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front
env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1)
- assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1']
+ assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1']
env['CLVar'] = CLVar([])
@@ -3125,6 +3125,29 @@ def generate(env):
assert t[4].path == 'p_ggg'
assert t[4].precious
+ def test_Pseudo(self):
+ """Test the Pseudo() method"""
+ env = self.TestEnvironment(FOO='ggg', BAR='hhh')
+ env.Dir('p_hhhb')
+ env.File('p_d')
+ t = env.Pseudo('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
+
+ assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
+ assert t[0].path == 'p_a'
+ assert t[0].pseudo
+ assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
+ assert t[1].path == 'p_hhhb'
+ assert t[1].pseudo
+ assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
+ assert t[2].path == 'p_c'
+ assert t[2].pseudo
+ assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
+ assert t[3].path == 'p_d'
+ assert t[3].pseudo
+ assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
+ assert t[4].path == 'p_ggg'
+ assert t[4].pseudo
+
def test_Repository(self):
"""Test the Repository() method."""
class MyFS(object):
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index a3016543..076ca65a 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -693,6 +693,15 @@ class NodeTestCase(unittest.TestCase):
node.set_precious(7)
assert node.precious == 7
+ def test_set_pseudo(self):
+ """Test setting a Node's pseudo value
+ """
+ node = SCons.Node.Node()
+ node.set_pseudo()
+ assert node.pseudo
+ node.set_pseudo(False)
+ assert not node.pseudo
+
def test_exists(self):
"""Test evaluating whether a Node exists.
"""
@@ -954,7 +963,7 @@ class NodeTestCase(unittest.TestCase):
self.source_scanner = scanner
builder = Builder2(ts1)
-
+
targets = builder([source])
s = targets[0].get_source_scanner(source)
assert s is ts1, s
@@ -967,7 +976,7 @@ class NodeTestCase(unittest.TestCase):
builder = Builder1(env=Environment(SCANNERS = [ts3]))
targets = builder([source])
-
+
s = targets[0].get_source_scanner(source)
assert s is ts3, s
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index c1142812..d353245d 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -58,6 +58,10 @@ from SCons.Debug import Trace
def classname(obj):
return str(obj.__class__).split('.')[-1]
+# Set to false if we're doing a dry run. There's more than one of these
+# little treats
+do_store_info = True
+
# Node states
#
# These are in "priority" order, so that the maximum value for any
@@ -215,6 +219,7 @@ class Node(object):
self.env = None
self.state = no_state
self.precious = None
+ self.pseudo = False
self.noclean = 0
self.nocache = 0
self.cached = 0 # is this node pulled from cache?
@@ -386,6 +391,13 @@ class Node(object):
self.clear()
+ if self.pseudo:
+ if self.exists():
+ raise SCons.Errors.UserError("Pseudo target " + str(self) + " must not exist")
+ else:
+ if not self.exists() and do_store_info:
+ SCons.Warnings.warn(SCons.Warnings.TargetNotBuiltWarning,
+ "Cannot find target " + str(self) + " after building")
self.ninfo.update(self)
def visited(self):
@@ -789,6 +801,10 @@ class Node(object):
"""Set the Node's precious value."""
self.precious = precious
+ def set_pseudo(self, pseudo = True):
+ """Set the Node's precious value."""
+ self.pseudo = pseudo
+
def set_noclean(self, noclean = 1):
"""Set the Node's noclean value."""
# Make sure noclean is an integer so the --debug=stree
diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py
index 9083b8ee..a81ac98a 100644
--- a/src/engine/SCons/Script/Main.py
+++ b/src/engine/SCons/Script/Main.py
@@ -1086,6 +1086,7 @@ def _build_targets(fs, options, targets, target_top):
SCons.Action.print_actions = not options.silent
SCons.Action.execute_actions = not options.no_exec
SCons.Node.FS.do_store_info = not options.no_exec
+ SCons.Node.do_store_info = not options.no_exec
SCons.SConf.dryrun = options.no_exec
if options.diskcheck:
diff --git a/src/engine/SCons/Script/Main.xml b/src/engine/SCons/Script/Main.xml
index b582e0d7..147e778a 100644
--- a/src/engine/SCons/Script/Main.xml
+++ b/src/engine/SCons/Script/Main.xml
@@ -685,6 +685,25 @@ Multiple targets can be passed in to a single call to
</summary>
</scons_function>
+<scons_function name="Pseudo">
+<arguments>
+(target, ...)
+</arguments>
+<summary>
+<para>
+This indicates that each given
+<varname>target</varname>
+should not be created by the build rule, and if the target is created,
+an error will be generated. This is similar to the gnu make .PHONY
+target. However, in the vast majority of cases, an
+&f-Alias;
+is more appropriate.
+
+Multiple targets can be passed in to a single call to
+&f-Pseudo;.
+</para>
+</summary>
+</scons_function>
<scons_function name="SetOption">
<arguments>
(name, value)
@@ -788,4 +807,4 @@ SetOption('max_drift', 1)
</summary>
</scons_function>
-</sconsdoc> \ No newline at end of file
+</sconsdoc>
diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py
index a260c4ac..ca6acee9 100644
--- a/src/engine/SCons/Warnings.py
+++ b/src/engine/SCons/Warnings.py
@@ -42,6 +42,9 @@ class WarningOnByDefault(Warning):
# NOTE: If you add a new warning class, add it to the man page, too!
+class TargetNotBuiltWarning(Warning): # Should go to OnByDefault
+ pass
+
class CacheWriteErrorWarning(Warning):
pass
diff --git a/test/Pseudo.py b/test/Pseudo.py
new file mode 100644
index 00000000..db3c30c0
--- /dev/null
+++ b/test/Pseudo.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#
+# __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 TestSCons
+
+test = TestSCons.TestSCons()
+
+# Firstly, build a pseudo target and make sure we get no warnings it
+# doesn't exist under any circumstances
+test.write('SConstruct', """
+env = Environment()
+env.Pseudo(env.Command('foo.out', [], '@echo boo'))
+""")
+
+test.run(arguments='-Q', stdout = 'boo\n')
+
+test.run(arguments='-Q --warning=target-not-built', stdout = "boo\n")
+
+# Now do the same thing again but create the target and check we get an
+# error if it exists after the build
+test.write('SConstruct', """
+env = Environment()
+env.Pseudo(env.Command('foo.out', [], Touch('$TARGET')))
+""")
+
+test.run(arguments='-Q', stdout = 'Touch("foo.out")\n', stderr = None,
+ status = 2)
+test.must_contain_all_lines(test.stderr(),
+ 'scons: *** Pseudo target foo.out must not exist')
+test.run(arguments='-Q --warning=target-not-built',
+ stdout = 'Touch("foo.out")\n',
+ stderr = None, status = 2)
+test.must_contain_all_lines(test.stderr(),
+ 'scons: *** Pseudo target foo.out must not exist')
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:
diff --git a/test/option/warn-dependency.py b/test/option/warn-dependency.py
index 95d8fed3..ca0c2aa8 100644
--- a/test/option/warn-dependency.py
+++ b/test/option/warn-dependency.py
@@ -43,6 +43,7 @@ env=Environment()
env['BUILDERS']['test'] = Builder(action=build,
source_scanner=SCons.Defaults.ObjSourceScan)
env.test(target='foo', source='foo.c')
+env.Pseudo('foo')
""")
test.write("foo.c","""
diff --git a/test/warning-TargetNotBuiltWarning.py b/test/warning-TargetNotBuiltWarning.py
new file mode 100644
index 00000000..76fdc5cd
--- /dev/null
+++ b/test/warning-TargetNotBuiltWarning.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+#
+# __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 TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+foo = Command('foo.out', [], '@echo boo')
+bill = Command('bill.out', [], Touch('$TARGET'))
+Depends(bill, foo)
+Alias('jim', bill)
+""")
+
+test.run(arguments='-Q jim', stdout = 'boo\nTouch("bill.out")\n')
+
+test.run(arguments='-Q jim --warning=target-not-built',
+ stdout = "boo\nscons: `jim' is up to date.\n",
+ stderr = None)
+test.must_contain_all_lines(test.stderr(),
+ 'scons: warning: Cannot find target foo.out after building')
+
+test.run(arguments='-Q jim --warning=target-not-built -n',
+ stdout = "scons: `jim' is up to date.\n")
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: