From 4585644bf8e880d29f0e5d8f281a3adda850bba8 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Sat, 18 Aug 2001 20:51:21 +0000 Subject: [PATCH] Implement error framework. git-svn-id: http://scons.tigris.org/svn/scons/trunk@24 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/MANIFEST | 1 + src/scons.py | 200 ++++++++++++++++++++++++--------------- src/scons/Errors.py | 18 ++++ src/scons/ErrorsTests.py | 28 ++++++ test/errors.py | 54 +++++++++++ 5 files changed, 223 insertions(+), 78 deletions(-) create mode 100644 src/scons/Errors.py create mode 100644 src/scons/ErrorsTests.py create mode 100644 test/errors.py diff --git a/src/MANIFEST b/src/MANIFEST index 64585a19..88709bac 100644 --- a/src/MANIFEST +++ b/src/MANIFEST @@ -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 diff --git a/src/scons.py b/src/scons.py index 11e12948..04292daa 100644 --- a/src/scons.py +++ b/src/scons.py @@ -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 index 00000000..2709c191 --- /dev/null +++ b/src/scons/Errors.py @@ -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 index 00000000..8d27332f --- /dev/null +++ b/src/scons/ErrorsTests.py @@ -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 index 00000000..d3b7e041 --- /dev/null +++ b/test/errors.py @@ -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() -- 2.26.2