Subversion Repositories DevTools

Rev

Rev 6619 | Rev 7219 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
4778 dpurdie 1
########################################################################
6177 dpurdie 2
# COPYRIGHT - VIX IP PTY LTD ("VIX"). ALL RIGHTS RESERVED.
4778 dpurdie 3
#
4
# Module name   : jats_runutf.pm
5
# Module type   : JATS Utility
6
# Compiler(s)   : Perl
7
# Environment(s): jats
8
#
9
# Description   : JATS Make Time Test Harness Support
10
#                 This package contains fucntions that will be used by JATS
11
#                 to invoke the tests.
12
#
13
#                 This is more powerful that the previous shell-based solution
14
#                 that had problems under windows.
15
#
16
#                 The functions are designed to be invoked as:
17
#                   $(GBE_PERL) -Mjats_runutf -e <function> -- <args>+
18
#
19
#                 The functions in this packages are designed to take parameters
20
#                 from @ARVG as this makes the interface easier to read.
21
#
22
# Usage         : See POD at the end of this file
23
#
24
#......................................................................#
25
 
26
require 5.008_002;
27
use strict;
28
use warnings;
29
 
30
package jats_runutf;
31
 
32
our (@ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS, $VERSION);
33
use Exporter;
34
use JatsError qw(:name=jats_runutf);
4780 dpurdie 35
use Pod::Usage;                             # required for help support
4778 dpurdie 36
use Getopt::Long;
37
use File::Spec;
5035 dpurdie 38
use XML::Simple;
4778 dpurdie 39
 
40
$VERSION = 1.00;
41
@ISA = qw(Exporter);
42
 
43
# Symbols to autoexport (:DEFAULT tag)
4780 dpurdie 44
@EXPORT = qw( processUtf help man );
4778 dpurdie 45
 
46
#
47
#   Global Variables
48
#
49
my $opt_verbose = $ENV{'GBE_VERBOSE'};      # Allow global verbose
4780 dpurdie 50
my $opt_help = 0;
4778 dpurdie 51
 
52
#   Data to be passed into the filter function
53
#   Defined values are:
4781 dpurdie 54
#       ARGS            - An hash of user arguments
55
#       DIR             - Optional. Name of test subdir
4778 dpurdie 56
#       FILTER          - Name of the filter 
4781 dpurdie 57
#       IDIR            - Path to original directory
4778 dpurdie 58
#       INTERFACE       - Abs Path to Interface directory
59
#       LOCAL           - Abs Path to Local directory
60
#       OUTDIR          - Abs Path to output directory
4780 dpurdie 61
#       OUTFILE         - Abs Path to suggested output file
4778 dpurdie 62
#       PKGDIR          - Abs Path to Packaging directory
63
#       ROOT            - Abs Path to Root of the build
64
#       TARGET          - Current make target
65
#       TYPE            - Built type P or D
5387 dpurdie 66
#       UTFUID          - Unique Test Identifier
4781 dpurdie 67
#       UTFNAME         - Test Name
6619 dpurdie 68
#       UTFTEST         - Recommended file root for test results
5035 dpurdie 69
#       UTFRC           - Result Code from Unit Test run
6898 dpurdie 70
#       RCFILE          - Path to the RC File (optional)
4778 dpurdie 71
#
4780 dpurdie 72
our %filterData;
4778 dpurdie 73
 
4780 dpurdie 74
#-------------------------------------------------------------------------------
75
# Function        : help
76
#                   man 
77
#
78
# Description     : Utility functions to make is easier for users to get POD
79
#                   from this module
80
#
81
# Inputs          : 
82
#
83
# Returns         : 
84
#
4778 dpurdie 85
 
4780 dpurdie 86
sub help
87
{
88
    pod2usage(-verbose => 0, -input =>  __FILE__);
89
}
90
 
91
sub man
92
{
93
    pod2usage(-verbose => 2, -input =>  __FILE__);
94
}
95
 
4778 dpurdie 96
#-------------------------------------------------------------------------------
97
# Function        : processUtf  
98
#
99
# Description     : Main function to process UTF results
4787 dpurdie 100
#                   This function will locate a suitable filter process and invoke
101
#                   it to process the results
4778 dpurdie 102
#
4787 dpurdie 103
#                   The filter process will be provided in a Perl Module
4778 dpurdie 104
#                   It may be a part of JATS or an external modules provided
4787 dpurdie 105
#                   within an external package. ie(utf may provide its own filter)
4778 dpurdie 106
#
4787 dpurdie 107
#
4778 dpurdie 108
# Inputs          : None. Parameters are passed via @ARGV
109
#
110
# Returns         : Nothing
111
#
112
 
113
sub processUtf
114
{
4780 dpurdie 115
    my $argCount = scalar @ARGV;
4778 dpurdie 116
    my $result = GetOptions (
4780 dpurdie 117
                    "help|h:+"      => \$opt_help,
118
                    "manual:3"      => \$opt_help,
4778 dpurdie 119
                    "verbose:+"     => \$opt_verbose,       # Only set to to 0,1 or 3
4780 dpurdie 120
                    "root=s"        => \$filterData{ROOT},
121
                    "filter=s"      => \$filterData{FILTER},
122
                    "interface=s"   => \$filterData{INTERFACE},
123
                    "local=s"       => \$filterData{LOCAL},
124
                    "target=s"      => \$filterData{TARGET},
125
                    "pkgdir=s"      => \$filterData{PKGDIR},
126
                    "type=s"        => \$filterData{TYPE},
4781 dpurdie 127
                    "dir=s"         => \$filterData{DIR},
6898 dpurdie 128
                    "rcfile=s"      => \$filterData{RCFILE},
129
                    "arg=s"         => sub {my ( $key, $value) = split('=', $_[1], 2); $filterData{ARGS}{uc $key} = $value;},
4778 dpurdie 130
                    );
131
 
4780 dpurdie 132
    pod2usage(-verbose => 0, -input =>  __FILE__) if ($opt_help == 1 || ! $result || $argCount < 1);
133
    pod2usage(-verbose => 1, -input =>  __FILE__) if ($opt_help == 2 );
134
    pod2usage(-verbose => 2, -input =>  __FILE__) if ($opt_help  > 2);
135
 
4778 dpurdie 136
    #   Reconfigure the verbosity level
137
    ErrorConfig( 'verbose', $opt_verbose);
4780 dpurdie 138
    Error("Internal: No Filter specified") unless defined $filterData{FILTER};
139
    Error("Internal: No PkgDir specified") unless defined $filterData{PKGDIR};
4778 dpurdie 140
 
141
    #
142
    #   Locate the required filter module
143
    #   Filter modules have a name of the form:
144
    #       UtfFilter_<FilterName>.pm
145
    #   And can be located:
146
    #       within JATS
147
    #           in 'TOOLS/LIB'
148
    #       within a Package declared
149
    #           with a BuildPkgArchive or a LinkPkgArchive
150
    #           within the packages 'tools/scripts' subdirectory
151
    #       within the current package
4996 dpurdie 152
    #           Perl modules with ROOT/gbe/utfFilters
4778 dpurdie 153
    #                         or in the current directory
154
 
4787 dpurdie 155
    my $module_name = join('_','UtfFilter', lc $filterData{FILTER});
4778 dpurdie 156
    Verbose("Filter Module: $module_name");
157
 
158
    #   Extend Perl Module search path for package-local filters
159
    #   Check the current directory
160
    #       The current directory is also within @INC, but it is at the end
161
    #       thus local filter will not override external filters. Place the
162
    #       current directory first - if it conatins a filter.
163
 
164
    if (-f "$module_name.pm" )
165
    {
166
        Verbose("Extend the filter module search path: Current Directory");
167
        unshift @INC, '.';
168
    }
169
    else
170
    {
171
        #
172
        #   Check package-local directory
173
        #       <root>/gbe/utfFilters
174
        #
4780 dpurdie 175
        my $localUtfPath = File::Spec->catfile($filterData{ROOT}, 'gbe', 'utfFilters');
4778 dpurdie 176
        if ( -f( "$localUtfPath/$module_name.pm") )
177
        {
178
            Verbose("Extend the filter module search path: $localUtfPath");
179
            unshift @INC, $localUtfPath;
180
        }
181
    }
182
 
183
    #
184
    #   Locate a Perl Module of the required name
185
    #
186
    eval "require $module_name";
187
    if ($@)
188
    {
189
        Error ("Could not load required filter module: $module_name");
190
    }
191
 
192
    #
193
    #   Ensure that the filter contains the required interface methods
194
    #
195
    foreach my $fname ( qw(processUtf))
196
    {
197
        ReportError("Required function DOES NOT exist: $fname")
198
            unless (defined($module_name->can($fname)));
199
    }
200
    ErrorDoExit();
201
 
202
    #
203
    #   Convert potentially local paths to absolute paths
204
    #       Simplifies use when the CWD is changed
205
    #
206
    foreach my $entry ( qw(INTERFACE LOCAL PKGDIR ROOT))
207
    {
4780 dpurdie 208
        $filterData{$entry}  = File::Spec->rel2abs($filterData{$entry} );
4778 dpurdie 209
    }
210
 
211
    #
212
    #   Add in known values from the environment
213
    #
4780 dpurdie 214
    $filterData{TYPE} = $ENV{'GBE_MAKE_TYPE'};
215
    Error("Internal: EnvVar 'GBE_MAKE_TYPE' not specified") unless $filterData{TYPE};
4778 dpurdie 216
 
5387 dpurdie 217
    $filterData{UTFUID} = $ENV{'GBE_UTFUID'};
218
    Error("Internal: EnvVar 'GBE_UTFUID' not specified") unless $filterData{UTFUID};
4781 dpurdie 219
 
220
    $filterData{UTFNAME} = $ENV{'GBE_UTFNAME'};
221
    Error("Internal: EnvVar 'GBE_UTFNAME' not specified") unless $filterData{UTFNAME};
222
 
4996 dpurdie 223
    $filterData{OUTFILE} = $ENV{'GBE_UTFFILE'};
224
    Error("Internal: EnvVar 'GBE_UTFFILE' not specified") unless $filterData{OUTFILE};
225
 
6619 dpurdie 226
    $filterData{UTFTEST} = $ENV{'GBE_UTFTEST'};
227
    Error("Internal: EnvVar 'GBE_UTFTEST' not specified") unless $filterData{UTFTEST};
228
 
4781 dpurdie 229
    $filterData{IDIR} = File::Spec->rel2abs('.');
230
 
4778 dpurdie 231
    #
4996 dpurdie 232
    # The environment provides a recommended (unqiue) output file
233
    #   Extact the directory part and ensure that it exists
234
    #   Brute file filename chop
4778 dpurdie 235
    #
4996 dpurdie 236
    $filterData{OUTDIR} = $filterData{OUTFILE};
237
    $filterData{OUTDIR} =~ s~/[^/]*$~~;
238
    Error("Internal: OUTDIR is empty") unless (length($filterData{OUTDIR}) > 1);
4778 dpurdie 239
 
4996 dpurdie 240
    mkdir $filterData{OUTDIR};
241
    Error("Creating utfResults directory", "Path: $filterData{OUTDIR}") unless -d $filterData{OUTDIR};
242
 
4781 dpurdie 243
    #   Allow the output file to be used
244
    #       Not in the build system, but in a user development area
4996 dpurdie 245
    #
246
    my $filename = $filterData{OUTFILE};
4781 dpurdie 247
    unlink $filename if -e $filename;
4996 dpurdie 248
    Error("Output file: Cannot delete $filename: $!") if -e $filename;
249
    Verbose("Writing output to: $filename");
4778 dpurdie 250
 
251
    #
4781 dpurdie 252
    #   Change to the test directory
253
    #       Only if required
254
    #       Ensure that the specified directory exists
255
    #
256
    if (defined $filterData{DIR})
257
    {
5035 dpurdie 258
        Verbose("Change directory:", $filterData{DIR});
4781 dpurdie 259
        Error("Internal: Test directory does not exist: $filterData{DIR}")
260
            unless (-d $filterData{DIR});
261
        chdir $filterData{DIR} || Error("Internal: Could not chdir to: $filterData{DIR}");
262
    }
263
 
264
    #
5035 dpurdie 265
    #   Recover the result code of the unit test run
266
    #
6898 dpurdie 267
    if (defined $filterData{RCFILE}) {
268
        my $rcFile = $filterData{RCFILE};
269
        Verbose("Result Code File:", $rcFile);
270
        if (-f $rcFile)
271
        {
272
            open( my $rcFile, '<', $rcFile) || Error ("Cannot open file : $!");
273
            $filterData{UTFRC} = <$rcFile>;
274
            $filterData{UTFRC} =~ s~\s+$~~;
275
            $filterData{UTFRC} =~ s~^\s+~~;
276
            Verbose("Recover Result Code: ", $filterData{UTFRC});
277
            close $rcFile;
278
        } else {
279
            Error("ResultCode file specified, but not found: $rcFile");
280
        }
281
    } 
5035 dpurdie 282
    #
4780 dpurdie 283
    #   Diagnostics
284
    #
285
    if (IsVerbose(1))
286
    {
287
        DebugDumpData("Filter Parameters", \%filterData);
288
    }
289
 
290
    #
4778 dpurdie 291
    #   Invoke the process method
292
    #   If it has a problem it should use 'Error(...)' to report it
4996 dpurdie 293
    #   There is no exit code processing, but if there is - needs to be false
4778 dpurdie 294
    #
4780 dpurdie 295
    Message("Processing UTF test results using filter: $filterData{FILTER}");
4996 dpurdie 296
    my $rv = $module_name->processUtf(\%filterData);
297
    Error ("Unit Test Failure: Errors detected in the result set")
298
        if ( defined($rv) && ! $rv );
4778 dpurdie 299
}
300
 
5035 dpurdie 301
#-------------------------------------------------------------------------------
302
# Function        : Write XML 
303
#
304
# Description     : Write user XML results to file
305
#                   Will insert standard data
306
#
307
# Inputs          : $options    - Ref to a hash of options
308
#                   $results    - Ref to an array of results
309
#                                 Expect a ref to an array of Hash Values
310
#
311
# Returns         : Nothing 
312
#
313
sub writeXmlResults
314
{
315
    my ($options, $results) = @_;
6898 dpurdie 316
 
317
    # Create a summary report - more for the user
318
    my $summary;
319
    my $total = 0;
320
    foreach my $entry ( @$results) {
321
        if (exists $entry->{OUTCOME}) {
322
            $summary->{$entry->{OUTCOME}}++;
323
            $total++;
324
        }
325
    }
326
    my $summaryString = '';
327
    my $joiner = '';
328
    foreach my $entry ( sort keys %{$summary}) {
329
        $summaryString .= $joiner . $entry . ':' . $summary->{$entry};
330
        $joiner = ', ';
331
    }
332
    Message("Total: $total. $summaryString");
333
 
334
 
5035 dpurdie 335
    #
336
    #   Create a data structure to contain the dummy test result
337
    #   Insert TARGET and TYPE attributes
338
    #
339
    my %xml;
340
    $xml{TestResults}{TARGET} = $options->{TARGET};
341
    $xml{TestResults}{TYPE} = $options->{TYPE};
342
 
343
    @{$xml{TestResults}{TestResult}} = @$results;
344
 
345
    #   The 'MESSAGE' key for failed tests forms the content of the
346
    #   <TestResult> element. Other keys are converted to attributes.
347
    #   Assign <TestResults> as the root XML node.
348
    my $xmlData = XMLout(\%xml, ContentKey => 'MESSAGE', RootName => 'TestResults', KeepRoot => 1);
349
 
350
    #   Write the data to the XML file.
351
    my $filename = $options->{OUTFILE};
352
    open ( my $outFile, ">", $filename)
353
        || Error(" Cannot open results file:$filename for writing: $!\n");
354
    print $outFile $xmlData;
355
    close $outFile;
356
}
357
 
358
 
4780 dpurdie 359
=pod 1
4778 dpurdie 360
 
4780 dpurdie 361
=for htmltoc    SYSUTIL::
362
 
363
=head1 NAME
364
 
365
jats_runutf - Post Process UTF results for build system
366
 
367
=head1 SYNOPSIS
368
 
369
  $(GBE_PERL) -Mjats_runutf -e processUtf -- <args>
370
 
371
 Options:
6898 dpurdie 372
    -help[=n]           - Brief help message
373
    -help -help         - Detailed help message
374
    -man                - Full documentation
375
    -verbose[=n]        - Verbose operation
376
    -filter=name        - Name of the required processing filter
377
    -target=name        - Current build target
378
    -root=path          - Path to the root of the build
379
    -pkgdir=path        - Path to the packaging directory
380
    -interface=path     - Path to the build interface directory
381
    -local=path         - Path to the local build directory
382
    -dir=path           - Path to test directory
383
    -rcfile=path        - Path to a file that contains the test result code
4780 dpurdie 384
 
385
=head1 OPTIONS
386
 
387
=over 8
388
 
389
=item B<-help>
390
 
391
Print a brief help message and exits.
392
 
393
=item B<-help -help>
394
 
395
Print a detailed help message with an explanation for each option.
396
 
397
=item B<-man>
398
 
399
Prints the manual page and exits.
400
 
401
=item B<-verbose>
402
 
403
This option will display progress information as the program executes.
404
 
405
=item B<-filter=name>
406
 
407
Name of the required processing filter.
408
 
409
=item B<-target=name>
410
 
411
The current build target.
412
 
413
=item B<-root=path>
414
 
415
The path to the root of the current build.
416
 
417
=item B<-pkgdir=path>
418
 
419
The path to the packaging directory
420
 
421
=item B<-interface=path>
422
 
423
The path to the build interface directory
424
 
425
=item B<-local=path>
426
 
427
The path to the local build directory
428
 
4781 dpurdie 429
=item B<-dir=path>
430
 
431
The path to the directory in which the test was run.
432
 
433
This is optional. If provided the filter will be invoked with the 
434
current working directory
435
 
6898 dpurdie 436
=item B<-rcfile=path>
437
 
438
The path to the file that will contain the rsult code of the test run.
439
This is optional, but if provided it should exist
440
 
4780 dpurdie 441
=back
442
 
443
=head1 DESCRIPTION
444
 
445
This tool is not designed to be run directly by users. It is intended to be run by the 
446
JATS generated makefiles in conjunction with unit tests to process the output from a unit 
447
test to provide a common output format to be passed on the build system.
448
 
449
Normally this process only occurs with the Auto BuildTool environment.
450
 
451
The tool provides the following operations:
452
 
453
=over 4
454
 
455
=item *
456
 
457
Sanitize environment
458
 
459
The environment passed to the filter processing module will be verified. 
460
The path to the output directory will be created if required. All paths will be absolute.
461
 
462
=item *
463
 
464
Locate the required filter processing module. The module must be a Perl Module with a name of the form 'UtfFilter_<name>'
465
 
466
=item *
467
 
468
Invoke the required filter module.
469
 
470
=back
471
 
472
=head2 Locating the Filter Module
473
 
474
The filter module may be located, in order of precedence, within:
475
 
476
=over 4
477
 
478
=item *
479
 
480
The package currently being built.
481
 
482
The package may provide its own UTF post processing module. This is not encouraged.
483
 
484
The following locations will be examined for a suitable module:
485
 
486
=over 4
487
 
488
=item *
489
 
490
The current directory. The directory of the makefile.pl that is running the unit test.
491
 
492
=item *
493
 
494
A directory in the Build Root called 'gbe/UtfFilters'
495
 
496
=back
497
 
498
=item *
499
 
500
An external Package, within the gbe/scripts directory.
501
 
502
The package can be specified with either LinkPkgArchive or BuildPkgArchive directive 
503
within the current packages build.pl file.
504
 
505
=item *
506
 
507
Within JATS.
508
 
509
Jats may provide useful filter modules.
510
 
511
=back
512
 
513
=head2 Filter Module Interface
514
 
515
The filter module Interface consists of four items:
516
 
517
=over 4
518
 
519
=item 1 The name of the Package
520
 
521
=item 1 The named function with the package
522
 
523
=item 1 Arguments passed to the named function
524
 
525
=item 1 The processing expected to be done by the named function
526
 
527
=item 1 The Output Format
528
 
529
=back
530
 
531
=head3 The name of the Package
532
 
533
Each filter function is in its own package. The package name is created by concatenating the 
534
text 'UtfFilter_' with the name of the required filter. 
535
 
536
ie: If the required filter is 'junit4', then the name of the filter package must 
537
be UtfFilter_junit4 and it will be held in a file named UtfFilter_junit4.pm.
538
 
539
=head3 The named function with the package
540
 
541
The filter package must provide a function called 'processUtf'
542
 
543
=head3 Arguments passed to the named function
544
 
545
The processing function 'processUtf' is called with two arguments:
546
 
547
=over 4
548
 
549
=item 1
550
 
551
The name of the package
552
 
553
=item 2
554
 
4787 dpurdie 555
A reference to a Perl hash. The hash will contain the following named items:
4780 dpurdie 556
 
557
=over 4
558
 
4781 dpurdie 559
=item       ARGS          
560
 
561
Optional. A hash of User Arguments passed into the filter. Use of these is filter specific.
562
 
563
Arguments of the form '--UtfArg=UserArg1=Value1' will be stored with a key of 'UserArg1 and a value of 'Value1'.
564
 
565
Arguments of the form '--UtfArg=UserArg2' will be stored with a key of 'UserArg2' and an undefined value.
566
 
567
=item       DIR          
568
 
569
Optional. If the Unit Test is executed in a subdirectory of the current build 
570
location, then DIR will be set to the name of the subdirectory. 
571
 
572
The current working directory will be changed to DIR before the filter function is invoked. 
573
 
574
This item will aways exist, but it may not be defined.
575
 
4780 dpurdie 576
=item       FILTER          
577
 
578
The Name of the filter 
579
 
4781 dpurdie 580
=item       IDIR
581
 
582
The absolute path to the working directory, when the module is invoked, before the working
4996 dpurdie 583
directory has been changed to 'DIR'.
4781 dpurdie 584
 
4780 dpurdie 585
=item       INTERFACE       
586
 
4781 dpurdie 587
The absolute path to Interface directory
4780 dpurdie 588
 
589
=item       LOCAL
590
 
4781 dpurdie 591
The absolute path to Local directory
4780 dpurdie 592
 
593
=item       OUTDIR
594
 
4781 dpurdie 595
The absolute path to output directory
4780 dpurdie 596
 
597
=item       OUTFILE
598
 
4781 dpurdie 599
The absolute path to suggested output file. The user does not need to use this name. It is 
4780 dpurdie 600
provided to remove the need for each filter to create a unique name.
601
 
602
This file will not exist.
603
 
604
=item       PKGDIR
605
 
4781 dpurdie 606
The absolute path to Packaging directory. This directory will exist.
4780 dpurdie 607
 
608
=item       ROOT
609
 
4781 dpurdie 610
The absolute path to Root of the build
4780 dpurdie 611
 
612
=item       TARGET
613
 
614
The current make target
615
 
616
=item       TYPE
617
 
618
The build type P or D
619
 
4781 dpurdie 620
=item       UTFNAME
621
 
622
The name of the test.
623
 
624
This may be provided by the user, or it may be system generated. Intended to be used by 
625
test filters that do not have test names generated as a part of the test
626
 
627
=item       UTFUID
628
 
629
A unique test identifier. This is unique with the build and is intended to:
630
 
631
=over 4
632
 
633
=item   *
634
 
635
Allow the generation of test-unique file names for the storage of results.
636
 
637
=item *
638
 
639
Allow the reuse of output file names.
640
 
4780 dpurdie 641
=back
642
 
5035 dpurdie 643
=item       UTFRC
644
 
645
The result code from the unit test run.
646
 
647
This will only be defined for JATS run unit tests.
648
 
4781 dpurdie 649
=back
650
 
4787 dpurdie 651
The return value from the function 'processUtf' is ignored. If the function encounters 
652
any error it should use the Jats 'Error' function to report the error.
4780 dpurdie 653
 
654
=back
655
 
656
=head3 The processing expected to be done by the named function
657
 
4787 dpurdie 658
The processing function is expected to transform the results of a unit test into 
659
a constient form so that they can be processed by the remainder of the build tool.
4780 dpurdie 660
 
661
The processing should:
662
 
663
=over 4
664
 
665
=item *
666
 
667
Create information in the OUTDIR directory. 
668
 
669
The filter may create a new file or insert information into an existing file. 
670
 
671
The user may make use of the OUTFILE path, but this is not mandatory.
672
 
673
=item *
674
 
675
Report errors by calling the Jats 'Error' function. This will terminate processing.
676
 
677
=back
678
 
679
=head3 The Output Format
680
 
681
Yet to be defined.
682
 
4781 dpurdie 683
The output format is known to the build system. It should not be 
684
changed without also chnaging it for the consuming tools.
685
 
4780 dpurdie 686
=cut
687
 
688
 
4778 dpurdie 689
1;