Provide an error message when a command isn't found.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 21 Dec 2001 17:46:00 +0000 (17:46 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 21 Dec 2001 17:46:00 +0000 (17:46 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@169 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
test/build-errors.py [new file with mode: 0644]

index cecf7173a9115bbda1d63d7b9b48ca266c97bbf3..4f161e4c335e0c4166f1e30c8884ce44cf7ab850 100644 (file)
@@ -15,6 +15,9 @@ RELEASE 0.02 -
 
   - Fix to setup.py so it doesn't require a sys.argv[1] argument.
 
+  - Provide make-like warning message for "command not found" and
+    similar errors.
+
   From Charles Crain:
 
   - Added support for the Install method.
index d22162953abfaa83fa9359814526f32f851cc4ac..2f937232a03dbbb96cd97120cf8fe81adc55d614 100644 (file)
@@ -36,6 +36,7 @@ import os.path
 import SCons.Node.FS
 from SCons.Util import PathList, scons_str2nodes, scons_subst, scons_subst_list, autogenerate
 import string
+import sys
 import types
 from UserList import UserList
 from UserDict import UserDict
@@ -48,6 +49,10 @@ except ImportError:
         pass
 
 
+exitvalmap = {
+    2 : 127,
+    13 : 126,
+}
 
 if os.name == 'posix':
 
@@ -55,7 +60,13 @@ if os.name == 'posix':
         pid = os.fork()
         if not pid:
             # Child process.
-            os.execvpe(cmd, args, env)
+            exitval = 127
+            try:
+                os.execvpe(cmd, args, env)
+            except OSError, e:
+                exitval = exitvalmap[e[0]]
+                sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+            os._exit(exitval)
         else:
             # Parent process.
             pid, stat = os.waitpid(pid, 0)
@@ -97,10 +108,14 @@ elif os.name == 'nt':
 
     def spawn(cmd, args, env):
         try:
-           ret = os.spawnvpe(os.P_WAIT, cmd, args, env)
-       except AttributeError:
-           cmd = pathsearch(cmd, env)
-           ret = os.spawnve(os.P_WAIT, cmd, args, env)
+            try:
+                ret = os.spawnvpe(os.P_WAIT, cmd, args, env)
+            except AttributeError:
+                cmd = pathsearch(cmd, env)
+                ret = os.spawnve(os.P_WAIT, cmd, args, env)
+        except OSError, e:
+            ret = exitvalmap[e[0]]
+            sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
         return ret
 
 
index 5beee1a9612c49fff3e9e2a667623bd120985db9..ad13dd76fef862a124d5c66eebcce8df8efec1c5 100644 (file)
@@ -36,6 +36,7 @@ import unittest
 
 import TestCmd
 import SCons.Builder
+import SCons.Errors
 
 # Initial setup of the common environment for all tests,
 # a temporary working directory containing a
@@ -125,11 +126,18 @@ class BuilderTestCase(unittest.TestCase):
        containing one of each.
        """
 
+        def MyBuilder(**kw):
+            builder = apply(SCons.Builder.Builder, (), kw)
+            def no_show(str):
+                pass
+            builder.action.show = no_show
+            return builder
+
        python = sys.executable
 
        cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
 
-       builder = SCons.Builder.Builder(action = cmd1)
+        builder = MyBuilder(action = cmd1)
        r = builder.execute()
        assert r == 0
        c = test.read(outfile, 'r')
@@ -137,7 +145,7 @@ class BuilderTestCase(unittest.TestCase):
 
        cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
 
-       builder = SCons.Builder.Builder(action = cmd2)
+        builder = MyBuilder(action = cmd2)
        r = builder.execute(target = 'foo')
        assert r == 0
        c = test.read(outfile, 'r')
@@ -145,7 +153,7 @@ class BuilderTestCase(unittest.TestCase):
 
        cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
 
-       builder = SCons.Builder.Builder(action = cmd3)
+        builder = MyBuilder(action = cmd3)
        r = builder.execute(target = ['aaa', 'bbb'])
        assert r == 0
        c = test.read(outfile, 'r')
@@ -153,7 +161,7 @@ class BuilderTestCase(unittest.TestCase):
 
        cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
 
-       builder = SCons.Builder.Builder(action = cmd4)
+        builder = MyBuilder(action = cmd4)
        r = builder.execute(source = ['one', 'two'])
        assert r == 0
        c = test.read(outfile, 'r')
@@ -161,7 +169,7 @@ class BuilderTestCase(unittest.TestCase):
 
        cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
 
-       builder = SCons.Builder.Builder(action = cmd4)
+        builder = MyBuilder(action = cmd4)
        r = builder.execute(source = ['three', 'four', 'five'])
        assert r == 0
        c = test.read(outfile, 'r')
@@ -169,7 +177,7 @@ class BuilderTestCase(unittest.TestCase):
 
        cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
 
-       builder = SCons.Builder.Builder(action = cmd5)
+        builder = MyBuilder(action = cmd5)
        r = builder.execute(target = 'out5', env = {'ENV' : {'XYZZY' : 'xyzzy'}})
        assert r == 0
        c = test.read(outfile, 'r')
@@ -183,7 +191,7 @@ class BuilderTestCase(unittest.TestCase):
 
         cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (python, act_py, outfile)
 
-        builder = SCons.Builder.Builder(action = cmd6)
+        builder = MyBuilder(action = cmd6)
         r = builder.execute(target = [Obj('111'), Obj('222')],
                             source = [Obj('333'), Obj('444'), Obj('555')])
         assert r == 0
@@ -195,7 +203,7 @@ class BuilderTestCase(unittest.TestCase):
         expect7 = '%s %s %s one\n%s %s %s two\n' % (python, act_py, outfile,
                                                     python, act_py, outfile)
 
-        builder = SCons.Builder.Builder(action = cmd7)
+        builder = MyBuilder(action = cmd7)
 
         global show_string
         show_string = ""
@@ -212,7 +220,7 @@ class BuilderTestCase(unittest.TestCase):
            open(kw['target'], 'w').write("function1\n")
            return 1
 
-       builder = SCons.Builder.Builder(action = function1)
+       builder = MyBuilder(action = function1)
        r = builder.execute(target = outfile)
        assert r == 1
        c = test.read(outfile, 'r')
@@ -222,7 +230,7 @@ class BuilderTestCase(unittest.TestCase):
            def __init__(self, **kw):
                open(kw['out'], 'w').write("class1a\n")
 
-       builder = SCons.Builder.Builder(action = class1a)
+       builder = MyBuilder(action = class1a)
        r = builder.execute(out = outfile)
        assert r.__class__ == class1a
        c = test.read(outfile, 'r')
@@ -233,7 +241,7 @@ class BuilderTestCase(unittest.TestCase):
                open(kw['out'], 'w').write("class1b\n")
                return 2
 
-       builder = SCons.Builder.Builder(action = class1b())
+       builder = MyBuilder(action = class1b())
        r = builder.execute(out = outfile)
        assert r == 2
        c = test.read(outfile, 'r')
@@ -254,12 +262,35 @@ class BuilderTestCase(unittest.TestCase):
            def __init__(self, **kw):
                open(kw['out'], 'a').write("class2b\n")
 
-       builder = SCons.Builder.Builder(action = [cmd2, function2, class2a(), class2b])
+       builder = MyBuilder(action = [cmd2, function2, class2a(), class2b])
        r = builder.execute(out = outfile)
        assert r.__class__ == class2b
        c = test.read(outfile, 'r')
         assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
 
+        # Test that a nonexistent command returns 127
+        builder = MyBuilder(action = python + "_XyZzY_")
+        r = builder.execute(out = outfile)
+        assert r == 127, "r == %d" % r
+
+        if os.name == 'nt':
+            # NT treats execs of directories and non-executable files
+            # as "file not found" errors
+            expect = 127
+        else:
+            expect = 126
+
+        # Test that trying to execute a directory returns 126
+        dir, tail = os.path.split(python)
+        builder = MyBuilder(action = dir)
+        r = builder.execute(out = outfile)
+        assert r == expect, "r == %d" % r
+
+        # Test that trying to execute a non-executable file returns 126
+        builder = MyBuilder(action = outfile)
+        r = builder.execute(out = outfile)
+        assert r == expect, "r == %d" % r
+
     def test_get_contents(self):
         """Test returning the signature contents of a Builder
         """
diff --git a/test/build-errors.py b/test/build-errors.py
new file mode 100644 (file)
index 0000000..e7bcdd7
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001 Steven Knight
+#
+# 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 os
+import os.path
+import TestCmd
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+no_such_file = test.workpath("no_such_file")
+not_executable = test.workpath("not_executable")
+
+test.write(not_executable, "\n")
+
+test.write("f1.in", "\n")
+test.write("f2.in", "\n")
+test.write("f3.in", "\n")
+
+test.write('SConstruct1', r"""
+bld = Builder(name = 'bld', action = '%s $SOURCES $TARGET')
+env = Environment(BUILDERS = [bld])
+env.bld(target = 'f1', source = 'f1.in')
+""" % os.path.normpath(no_such_file))
+
+test.run(arguments='-f SConstruct1 .',
+        stdout = "%s f1.in f1\n" % no_such_file,
+        stderr = """scons: %s: No such file or directory
+scons: *** [f1] Error 127
+""" % no_such_file)
+
+test.write('SConstruct2', r"""
+bld = Builder(name = 'bld', action = '%s $SOURCES $TARGET')
+env = Environment(BUILDERS = [bld])
+env.bld(target = 'f2', source = 'f2.in')
+""" % os.path.normpath(not_executable))
+
+if os.name == 'nt':
+    expect = """scons: %s: No such file or directory
+scons: *** [f2] Error 127
+""" % not_executable
+else:
+    expect = """scons: %s: Permission denied
+scons: *** [f2] Error 126
+""" % not_executable
+
+test.run(arguments='-f SConstruct2 .',
+        stdout = "%s f2.in f2\n" % not_executable,
+        stderr = expect)
+
+test.write('SConstruct3', r"""
+bld = Builder(name = 'bld', action = '%s $SOURCES $TARGET')
+env = Environment(BUILDERS = [bld])
+env.bld(target = 'f3', source = 'f3.in')
+""" % os.path.normpath(test.workdir))
+
+test.run(arguments='-f SConstruct3 .',
+        stdout = "%s f3.in f3\n" % test.workdir,
+        stderr = """scons: %s: Permission denied
+scons: *** [f3] Error 126
+""" % test.workdir)
+
+test.pass_test()