Implement error framework.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 18 Aug 2001 20:51:21 +0000 (20:51 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sat, 18 Aug 2001 20:51:21 +0000 (20:51 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@24 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/MANIFEST
src/scons.py
src/scons/Errors.py [new file with mode: 0644]
src/scons/ErrorsTests.py [new file with mode: 0644]
test/errors.py [new file with mode: 0644]

index 64585a191ce2d2da356e57230a4b05262b334bcd..88709bac4291fc6ff9fecfae685204988b21eeae 100644 (file)
@@ -3,6 +3,7 @@ scons/__init__.py
 scons/Builder.py
 scons/Defaults.py
 scons/Environment.py
+scons/Errors.py
 scons/Job.py
 scons/Node/__init__.py
 scons/Node/FS.py
index 11e12948e0dd42aeb6effa17252e3abb7fc4f457..04292daa5318cf994cf16fafe9dbdc066f67525a 100644 (file)
@@ -1,88 +1,19 @@
-#!/usr/bin/env python
+#! /usr/bin/env python
 
 import getopt
 import os.path
 import string
 import sys
-
-def PrintUsage():
-    print "Usage: scons [OPTION]... TARGET..."
-    print "Build TARGET or multiple TARGET(s)"
-    print " "
-    print '  -f CONSCRIPT        execute CONSCRIPT instead of "SConstruct"'
-    print "  -j N                execute N parallel jobs"
-    print "  --help              print this message and exit"
-
-try:
-    opts, targets = getopt.getopt(sys.argv[1:], 'f:j:', ['help'])
-except getopt.GetoptError, x:
-    print x
-    PrintUsage()
-    sys.exit()
-
-Scripts = []
-
-num_jobs = 1
-for o, a in opts:
-    if o == '-f': Scripts.append(a)
-
-    if o == '-j':
-        try:
-            num_jobs = int(a)
-        except:
-            PrintUsage()
-            sys.exit(1)
-
-        if num_jobs <= 0:
-            PrintUsage()
-            sys.exit(1)
-            
-    if o == '--help':
-        PrintUsage()
-        sys.exit(0)
-    
-if not Scripts:
-    Scripts.append('SConstruct')
-
-
-# XXX The commented-out code here adds any "scons" subdirs in anything
-# along sys.path to sys.path.  This was an attempt at setting up things
-# so we can import "node.FS" instead of "scons.Node.FS".  This doesn't
-# quite fit our testing methodology, though, so save it for now until
-# the right solutions pops up.
-#
-#dirlist = []
-#for dir in sys.path:
-#    scons = os.path.join(dir, 'scons')
-#    if os.path.isdir(scons):
-#     dirlist = dirlist + [scons]
-#    dirlist = dirlist + [dir]
-#
-#sys.path = dirlist
-
+import traceback
 
 from scons.Node.FS import init, Dir, File, lookup
 from scons.Environment import Environment
 import scons.Job
 from scons.Builder import Builder
-
-init()
-
-
-
-def Conscript(filename):
-    Scripts.append(filename)
-
-
-
-while Scripts:
-    file, Scripts = Scripts[0], Scripts[1:]
-    execfile(file)
-
-
+from scons.Errors import *
 
 class Task:
-    "this is here only until the build engine is implemented"
+    "XXX: this is here only until the build engine is implemented"
 
     def __init__(self, target):
         self.target = target
@@ -93,7 +24,7 @@ class Task:
 
 
 class Taskmaster:
-    "this is here only until the build engine is implemented"
+    "XXX: this is here only until the build engine is implemented"
 
     def __init__(self, targets):
         self.targets = targets
@@ -118,10 +49,123 @@ class Taskmaster:
         pass
 
 
-taskmaster = Taskmaster(map(lambda x: lookup(File, x), targets))
+# Global variables
+
+Scripts = []
+
+# utility functions
+
+def _scons_syntax_error(e):
+    """Handle syntax errors. Print out a message and show where the error
+    occurred.
+    """
+    etype, value, tb = sys.exc_info()
+    lines = traceback.format_exception_only(etype, value)
+    for line in lines:
+        sys.stderr.write(line+'\n')
+
+def _scons_user_error(e):
+    """Handle user errors. Print out a message and a description of the
+    error, along with the line number and routine where it occured.
+    """
+    print 'user error'
+    etype, value, tb = sys.exc_info()
+    while tb.tb_next is not None:
+        tb = tb.tb_next
+    lineno = traceback.tb_lineno(tb)
+    filename = tb.tb_frame.f_code.co_filename
+    routine = tb.tb_frame.f_code.co_name
+    sys.stderr.write("\nSCons error: %s\n" % value)
+    sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
+
+def _scons_other_errors():
+    """Handle all errors but user errors. Print out a message telling
+    the user what to do in this case and print a normal trace.
+    """
+    print 'other errors'
+    traceback.print_exc()
+
+
+def PrintUsage():
+    print "Usage: scons [OPTION]... TARGET..."
+    print "Build TARGET or multiple TARGET(s)"
+    print " "
+    print '  -f CONSCRIPT        execute CONSCRIPT instead of "SConstruct"'
+    print "  -j N                execute N parallel jobs"
+    print "  --help              print this message and exit"
+
+def Conscript(filename):
+    Scripts.append(filename)
+
+def main():
+    global Scripts
+
+    try:
+        opts, targets = getopt.getopt(sys.argv[1:], 'f:j:', ['help'])
+    except getopt.GetoptError, x:
+        print x
+        PrintUsage()
+        sys.exit(1)
+
+    num_jobs = 1
+    for o, a in opts:
+        if o == '-f': Scripts.append(a)
 
-jobs = scons.Job.Jobs(num_jobs, taskmaster)
-jobs.start()
-jobs.wait()
+        if o == '-j':
+            try:
+                num_jobs = int(a)
+            except:
+                PrintUsage()
+                sys.exit(1)
 
+            if num_jobs <= 0:
+                PrintUsage()
+                sys.exit(1)
 
+        if o == '--help':
+            PrintUsage()
+            sys.exit(0)
+
+    if not Scripts:
+        Scripts.append('SConstruct')
+
+
+    # XXX The commented-out code here adds any "scons" subdirs in anything
+    # along sys.path to sys.path.  This was an attempt at setting up things
+    # so we can import "node.FS" instead of "scons.Node.FS".  This doesn't
+    # quite fit our testing methodology, though, so save it for now until
+    # the right solutions pops up.
+    #
+    #dirlist = []
+    #for dir in sys.path:
+    #    scons = os.path.join(dir, 'scons')
+    #    if os.path.isdir(scons):
+    #     dirlist = dirlist + [scons]
+    #    dirlist = dirlist + [dir]
+    #
+    #sys.path = dirlist
+
+    # initialize node factory
+    init()
+
+    while Scripts:
+        file, Scripts = Scripts[0], Scripts[1:]
+        execfile(file)
+
+    taskmaster = Taskmaster(map(lambda x: lookup(File, x), targets))
+
+    jobs = scons.Job.Jobs(num_jobs, taskmaster)
+    jobs.start()
+    jobs.wait()
+
+if __name__ == "__main__":
+    try:
+        main()
+    except KeyboardInterrupt:
+        print "Build interrupted."
+    except SyntaxError, e:
+        _scons_syntax_error(e)
+    except UserError, e:
+        _scons_user_error(e)
+    except:
+        _scons_other_errors()
diff --git a/src/scons/Errors.py b/src/scons/Errors.py
new file mode 100644 (file)
index 0000000..2709c19
--- /dev/null
@@ -0,0 +1,18 @@
+"""scons.Errors
+
+This file contains the exception classes used to handle internal
+and user errors in scons.
+
+"""
+
+__revision__ = "Errors.py __REVISION__ __DATE__ __DEVELOPER__"
+
+
+
+class InternalError(Exception):
+    def __init__(self, args=None):
+        self.args = args
+
+class UserError(Exception):
+    def __init__(self, args=None):
+        self.args = args
diff --git a/src/scons/ErrorsTests.py b/src/scons/ErrorsTests.py
new file mode 100644 (file)
index 0000000..8d27332
--- /dev/null
@@ -0,0 +1,28 @@
+__revision__ = "ErrorsTests.py __REVISION__ __DATE__ __DEVELOPER__"
+
+import sys
+import unittest
+from scons.Errors import InternalError, UserError
+
+
+class ErrorsTestCase(unittest.TestCase):
+    def test_InternalError(self):
+       """Test the InternalError exception."""
+        try:
+            raise InternalError, "test internal error"
+        except InternalError, e:
+            assert e.args == "test internal error"
+
+    def test_UserError(self):
+       """Test the UserError exception."""
+        try:
+            raise UserError, "test user error"
+        except UserError, e:
+            assert e.args == "test user error"
+
+
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(ErrorsTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+       sys.exit(1)
diff --git a/test/errors.py b/test/errors.py
new file mode 100644 (file)
index 0000000..d3b7e04
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+__revision__ = "test/t0003.py __REVISION__ __DATE__ __DEVELOPER__"
+
+from TestCmd import TestCmd
+
+test = TestCmd(program = 'scons.py',
+               workdir = '',
+               interpreter = 'python')
+
+test.write('SConstruct1', """
+a ! int(2.0)
+""")
+test.run(chdir = '.', arguments='-f SConstruct1')
+test.fail_test(test.stderr() != """  File "SConstruct1", line 2
+
+    a ! int(2.0)
+
+      ^
+
+SyntaxError: invalid syntax
+
+""")
+
+
+test.write('SConstruct2', """
+raise UserError, 'Depends() require both sources and targets.'
+""")
+test.run(chdir = '.', arguments='-f SConstruct2')
+test.fail_test(test.stderr() != """
+SCons error: Depends() require both sources and targets.
+File "SConstruct2", line 2, in ?
+""")
+
+
+import os
+sconspath = os.path.join(os.getcwd(), 'scons.py')
+
+test.write('SConstruct3', """
+raise InternalError, 'error inside'
+""")
+test.run(chdir = '.', arguments='-f SConstruct3')
+expect = r"""Traceback \((most recent call|innermost) last\):
+  File "%s", line 163, in \?
+    main\(\)
+  File "%s", line 153, in main
+    execfile\(file\)
+  File "SConstruct3", line 2, in \?
+    raise InternalError, 'error inside'
+InternalError: error inside
+""" % (sconspath, sconspath)
+test.fail_test(not test.match_re(test.stderr(), expect))
+
+test.pass_test()