Rev 2429 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
######################################################################### Copyright ( C ) 2004-2009 ERG Limited, All rights reserved## Module name : JatsError# Module type : Perl Package# Compiler(s) : Perl# Environment(s): jats## Description : A Perl Package to perform error handling within JATS## Uses global variables# $::ScmWho;# $::ScmVerbose;# $::ScmQuiet;# $::ScmDebug;# For use with existing scripts###......................................................................#package JatsError;use base qw(Exporter);require 5.006_001;use strict;use warnings;use Data::Dumper;use IO::Handle;#-------------------------------------------------------------------------------# Function : BEGIN## Description : Standard Package Interface## Inputs :## Returns :#BEGIN {our ($VERSION, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);# set the version for version checking$VERSION = 1.00;@EXPORT = qw(ErrorConfig ErrorReConfig ErrorDoExitReportError Fatal Error WarningMessage Message1Information Information1QuestionVerbose0 Verbose Verbose2 Verbose3Debug0 Debug Debug2 Debug3IsVerbose IsDebug IsQuietDebugDumpData DebugDumpPackage DebugTraceBackDebugPush DebugPop);%EXPORT_TAGS = ( ); # eg: TAG => [ qw!name1 name2! ],# your exported package globals go here,# as well as any optionally exported functions@EXPORT_OK = qw();## Ensure globals have a defined value#$::ScmWho = '' unless defined( $::ScmWho );$::ScmVerbose = $ENV{GBE_VERBOSE} || 0 unless defined( $::ScmVerbose );$::ScmDebug = $ENV{GBE_DEBUG} || 0 unless defined( $::ScmDebug );$::ScmQuiet = 0 unless defined( $::ScmQuiet );## Force autoflush in an attempt to limit the intermingling of# Error and non-error output.#STDOUT->autoflush(1);STDERR->autoflush(1);}# exported package globals go here#our $ScmWho;#our $ScmVerbose;#our $ScmDebug;#our $ScmQuiet;our $ScmOnExit;our $ScmDelayExit;our $ScmErrorCount;our $ScmExitCode;# non-exported package globals go here$ScmErrorCount = 0;$ScmExitCode = 1;my $EName = '';my $EFn = '';# initialize package globals, first exported ones#-------------------------------------------------------------------------------# Function : import## Description : Package import function# This function will examine arguments provided in the# invoking 'uses' list and will configure the package# accordingly.## Inputs : $pack - Name of this package# @vars - User Config Options# Config Options:# :name=xxxx# :function=xxxx# :quiet=xxx# :debug=xxx# :verbose=xxx# :delay_exit=xxx## Returns :#sub import {my $pack = shift;my @vars;my @config;## Extract options of the form: :name=value and pass them to the# ErrorConfig function. All other arguments will be passed to the#foreach ( @_ ){if ( m/^:(.+)=(.+)/ ) {push @config, $1, $2;} else {push @vars, $_;}}ErrorConfig( @config )if ( @config );## Invoke Exporter function to handle the arguments that I don't understand#$pack->export_to_level(1, $pack , @vars);}#-------------------------------------------------------------------------------# Function : ErrorConfig## Description : Configure aspects of the JATS error handle# See ErrorReConfig## Inputs : A hash of option,value pairs# Valid options# name - Name to report in error# function - Name of enclosing function# verbose - vebosity level# debug - debug level# on_exit - Register on-exit function# delay_exit - Delay exit on error## Returns :#sub ErrorConfig{my %args = @_;while (my($key, $value) = each %args){if ( $key =~ /^name/ ) {$EName = $value;} elsif ( $key =~ /^function/ ) {$EFn = ':' . $value;} elsif ( $key =~ /^debug/ ) {$::ScmDebug = $valueif ( defined $value && $value > $::ScmDebug );} elsif ( $key =~ /^verbose/ ) {$::ScmVerbose = $valueif ( defined $value && $value > $::ScmVerbose );} elsif ( $key =~ /^quiet/ ) {$::ScmQuiet = $value || 0;} elsif ( $key =~ /^on_exit/ ) {$ScmOnExit = $value;} elsif ( $key =~ /^delay_exit/ ) {$ScmDelayExit = $value;} elsif ( $key =~ /^exitCode/i ) {$ScmExitCode = $value || 1;} else {Error("ErrorConfig, Unknown option: $key");}}## Calculate the prefix to all messages# Based on Name and Function( if provided#$::ScmWho = "[$EName$EFn] ";## Extract program specfic debug flags from the environment# These will be based on the reporting 'name'# GBE_name_DEBUG# GBE_name_VERBOSE#if ( $EName ){my ($value, $tag);$tag = "GBE_${EName}_DEBUG" ;$tag =~ s~\s+~~g;$value = $ENV{ $tag };if (defined $value){$::ScmDebug = $value;Warning("Envar: $tag setting debug: $value");}$tag = "GBE_${EName}_VERBOSE" ;$tag =~ s~\s+~~g;$value = $ENV{ $tag };if (defined $value){$::ScmVerbose = $value;Warning("Envar: $tag setting verbose: $value");}}## Sanitise quiet and verbose# Any verboseness disables quiet#$::ScmQuiet = 0 if ( $::ScmVerbose );$::ScmQuiet = 0 if ( $::ScmDebug );}#-------------------------------------------------------------------------------# Function : ErrorReConfig## Description : Similar to ErrorConfig , except it is used to push and# automatically pop the current state## Intended to be used to control error reporting# within a function. Let the class go out of scope# at the end of the function.## Not intended that the user hold and pass around the# class ref as this may confuse all.## Inputs : As for ErrorConfig## Returns : Ref to a class# When this goes out of scope the Error State will be# restored.#sub ErrorReConfig{## Create a small class to hold existing Error Information# The error information will be restored when the handle returned to# the user goes out of scope.#my $self;$self->{ScmWho} = $::ScmWho;$self->{ScmVerbose} = $::ScmVerbose;$self->{ScmDebug} = $::ScmDebug;$self->{ScmQuiet} = $::ScmQuiet;$self->{ScmOnExit} = $ScmOnExit;$self->{ScmDelayExit} = $ScmDelayExit;$self->{ScmErrorCount} = $ScmErrorCount;$self->{ScmExitCode} = $ScmExitCode;$self->{EName} = $EName;$self->{EFn} = $EFn;bless ($self, __PACKAGE__);## Invoke ErrorConfig to do the hard work#ErrorConfig (@_);## Return ref to stored data#return $self;}#-------------------------------------------------------------------------------# Function : DESTROY## Description : Called when the handle retruned by ErrorConfig goes out of# scope.## Restores the state of the Error Reporting information## Inputs : $self - Created by ErrorReConfig## Returns : Nothing#sub DESTROY{my ($self) = @_;$::ScmWho = $self->{ScmWho};$::ScmVerbose = $self->{ScmVerbose};$::ScmDebug = $self->{ScmDebug};$::ScmQuiet = $self->{ScmQuiet};$ScmOnExit = $self->{ScmOnExit};$ScmDelayExit = $self->{ScmDelayExit};$ScmErrorCount = $self->{ScmErrorCount};$ScmExitCode = $self->{ScmExitCode};$EFn = $self->{EFn};$EName = $self->{EName};}#-------------------------------------------------------------------------------# Function : Information# Message# Question# Warning# Error# Verbose# Debug# _Message ( Internal use only )## Description : Error, Warning and Message routines# These routines will display a message to the user# with the module name.## Multiple arguments are displayed on their own line# with suitable spacing.## Inputs : Lines of text to display## Returns : Nothing#sub _Message{my $tag = shift; # First argument is a tagmy $count = 0;## Generate the message prefix# This will only be used on the first line# All other lines will have a space filled prefix#my $prefix = $::ScmWho . $tag;## Kill the eol if the Question is being asked#my $eol = ( $tag =~ m/Q/ ) ? "" : "\n";foreach my $nextline ( @_ ){next unless ( defined $nextline ); # Ignore undefined argumentschomp( my $line = $nextline );if ( $count == 1 ){my $bol = $eol ? "" : "\n";$prefix = $bol . ' ' x length($prefix);}print "$prefix $line$eol";$count++;}}#-------------------------------------------------------------------------------# Function : Information# Information1## Description : Will display informational messages# These are normal operational messages. These may be# supressed through the use of QUIET options## Inputs : An array of strings to display#sub Information{_Message '(I)', @_ unless ( $::ScmQuiet);}sub Information1{_Message '(I)', @_ unless ( $::ScmQuiet > 1);}#-------------------------------------------------------------------------------# Function : Message# Message1## Description : Same as Information, except a different prefix## Inputs : An array of strings to display#sub Message{_Message '(M)', @_ unless ( $::ScmQuiet > 1);}sub Message1{_Message '(M)', @_ unless ( $::ScmQuiet);}#-------------------------------------------------------------------------------# Function : IsQuiet## Description : Determine if an Infrmation or Message will be displayed# May be used to reduce excessive processing that may be# discarded## Inputs : $level - Level to test## Returns : TRUE: A Message at $level would be displayed#sub IsQuiet{my( $level) = @_;return $::ScmQuiet >= $level;}#-------------------------------------------------------------------------------# Function : Warning## Description : Display a warning message# These may be disabled with a high quiet level## Inputs : An array of strings to display#sub Warning{_Message '(W)', @_ unless ( $::ScmQuiet > 2);}#-------------------------------------------------------------------------------# Function : Question## Description : Display a Question message# These cannot be disabled## Inputs : An array of strings to display#sub Question{_Message '(Q)', @_;STDERR->flush; # Force output to be displayedSTDOUT->flush; # Force output to be displayed}#-------------------------------------------------------------------------------# Function : Fatal## Description : Display a multi line fatal message# This will cause the program to exit.## Similar to Error(), except# Display a (F) prefix# Alters the exit code to "2"# Will terminate program execution.# Will not honor delayed exit configuration.## Fatal is to be used to indicate to consumer processes that# the error is a function of the infrastructure and cannot be# corrected by a user. ie:# clearcase is not available# Not just a bad user parameter# dpkg_archive is not available# release manager database is not available## Intended to be used by build deamons to determine if building# should continue, or if the entire build process should be# terminated.## Inputs : An array of strings to display## Returns : May not return#sub Fatal{_Message '(F)', @_;$ScmErrorCount++;$ScmExitCode = 2;ErrorDoExit() unless ( $ScmDelayExit );}#-------------------------------------------------------------------------------# Function : Error## Description : Display a multi line error message# This may cause the program to exit, or the user may have# configured the package to accumulate error messages## This could be used to generate multiple error messages# while parsing a file, and then terminate program execution at# the end of the phase.## Inputs : An array of strings to display# First entry May be an exist code of the form# ExitCode=nnn## Returns : May not return#sub Error{if ( $_[0] =~ m~^ExitCode=(\d+)$~i ){$ScmExitCode = $1 || 1;shift @_;}_Message '(E)', @_;$ScmErrorCount++;ErrorDoExit() unless ( $ScmDelayExit );}#-------------------------------------------------------------------------------# Function : ReportError## Description : Like Error, but the error exit is delayed## Inputs : An array of strings to display#sub ReportError{_Message '(E)', @_;$ScmErrorCount++;}#-------------------------------------------------------------------------------# Function : ErrorDoExit## Description : Will terminate the program if delayed error messages# have been seen.## Inputs : None## Returns : Will return if no errors have been reported#sub ErrorDoExit{if ( $ScmErrorCount ){## Prevent recusion.# Kill error processing while doing error exit processing#if ( my $func = $ScmOnExit ){$ScmOnExit = undef;&$func( $ScmExitCode );}exit $ScmExitCode;}}#-------------------------------------------------------------------------------# Function : ArgsToString## Description : Convert an array of arguments to a string# Main purpose is to allow Debug and Verbose# calls to pass undef values without causing warnings## Inputs : REF to a list of scalar values# Passing a REF is faster## Returns : A string#sub ArgsToString{my $result = '';$result .= (defined ($_) ? $_ : '\'undef\'') . ' ' foreach ( @{$_[0]} );return $result;}#-------------------------------------------------------------------------------# Function : Verbose0# Verbose# Verbose2# Verbose3## Description : Various levels of progress reporting# By default none are displayed## Inputs : A single line string# Multi-line output is not supported# Arguments will be processed such that undef is handled well#sub Verbose0{_Message '------', ArgsToString (\@_);}sub Verbose{_Message '(V)', ArgsToString (\@_) if ($::ScmVerbose);}sub Verbose2{_Message '(V)', ArgsToString (\@_) if ($::ScmVerbose >= 2);}sub Verbose3{_Message '(V)', ArgsToString (\@_) if ($::ScmVerbose >= 3);}sub IsVerbose{my( $level) = @_;return $::ScmVerbose >= $level;}#-------------------------------------------------------------------------------# Function : Debug# Debug0# Debug1# Debug2# Debug3## Description : Various levels of debug reporting# By default none are displayed## Inputs : A single line string# Multi-line output is not supported# Arguments will be processed such that undef is handled well#sub Debug0{_Message '------', ArgsToString (\@_);}sub Debug{_Message '(D)', ArgsToString (\@_) if ($::ScmDebug >= 1 ) ;}sub Debug2{_Message '(D)', ArgsToString (\@_) if ($::ScmDebug >= 2) ;}sub Debug3{_Message '(D)', ArgsToString (\@_) if ($::ScmDebug >= 3) ;}sub IsDebug{my( $level) = @_;return $::ScmDebug >= $level;}#-------------------------------------------------------------------------------# Function : DebugDumpData## Description : Dump a data structure## Inputs : $name - A name to give the structure# @refp - An array of references## Returns :#sub DebugDumpData{my ($name, @refp) = @_;my $ii = 0;$Data::Dumper::Sortkeys = 1;foreach ( @refp ){print Data::Dumper->Dump ( [$_], ["*[Arg:$ii] $name" ]);$ii++}}#-------------------------------------------------------------------------------# Function : DebugTraceBack## Description : Display the call stack## Inputs : $tag## Returns : Nothing#sub DebugTraceBack{my ($tag) = @_;$tag = 'TraceBack' unless ( $tag );## Limit the stack stace.# It can't go on forever#foreach my $ii ( 0 .. 20 ){my ($package, $filename, $line) = caller($ii);last unless ( $package );print "$tag: $ii: $package, $filename, $line\n";}}#-------------------------------------------------------------------------------# Function : DebugPush## Description : Save the current debug level and then use a new name and# debug level for future reporting## Provided for backward compatability# Preferred solution is ErrorReConfig## Inputs : $name - New program name# $level - New program debug level## Returns : Current debug level#my @DebugStack = ();sub DebugPush{my ($name, $new_level) = @_;my %args;## Save current state on a stack#my $estate = ErrorReConfig ();push @DebugStack, $estate;$::ScmDebug = $new_level if ( defined $new_level && $new_level );$::ScmWho = $name if ( defined $name && $name );return $::ScmDebug;}#-------------------------------------------------------------------------------# Function : DebugPop## Description : Restores the operation of the DebugPush## Inputs : None#sub DebugPop{pop @DebugStack;}#-------------------------------------------------------------------------------# Function : DebugDumpPackage## Description : Dump data within the scope of a given package## Inputs : $packageName - To dump## Returns :#sub DebugDumpPackage{no strict "vars";no strict "refs";my ($packageName) = @_;print "==DebugDumpPackage: $packageName =============================\n";local $Data::Dumper::Pad = "\t ";local $Data::Dumper::Maxdepth = 2;local $Data::Dumper::Indent = 1;# We want to get access to the stash corresponding to the package name*stash = *{"${packageName}::"}; # Now %stash is the symbol table# $, = " "; # Output separator for print# Iterate through the symbol table, which contains glob values# indexed by symbol names.while (my ($varName, $globValue) = each %stash){print "$varName =============================\n";next if ( $varName eq 'stash' );local *alias = $globValue;if (defined ($alias)) {print Data::Dumper->Dump ( [$alias], ["*$varName" ]);# print "\t \$$varName $alias \n";}if (@alias) {print Data::Dumper->Dump ( [\@alias], ["*$varName" ]);# print "\t \@$varName @alias \n";}if (%alias) {print Data::Dumper->Dump ( [\%alias], ["*$varName" ]);# print "\t \%$varName ",%alias," \n";}if (defined (&alias)) {# print Data::Dumper->Dump ( [\&alias], ["*$varName" ]);print "\t \&$varName ","Code Fragment"," \n";}}}##1;