File Coverage

blib/lib/Env/Modulecmd.pm
Criterion Covered Total %
statement 57 74 77.0
branch 6 24 25.0
condition 5 15 33.3
subroutine 9 9 100.0
pod n/a
total 77 122 63.1


line stmt bran cond sub pod time code
1             # $Id: Modulecmd.pm,v 5.3 2014/08/18 16:56:11 ronisaac Exp $
2              
3             # Copyright (c) 2001-2014, Morgan Stanley.
4             # Distributed under the terms of the GNU General Public License.
5             # Please see the copyright notice at the end of this file for more information.
6              
7             package Env::Modulecmd;
8              
9             BEGIN {
10             # defaults: if Env::Modulecmd is built using perl5.005 or later, the
11             # magic strings below are replaced with values supplied to 'make' at
12             # build time
13              
14 1     1   5313 my $modulecmd = '';
15 1         2 my $modulepath = '';
16              
17 1 50 33     14 $ENV{PERL_MODULECMD} ||= $modulecmd unless ($modulecmd =~ /^\@\@/);
18 1 50 33     31 $ENV{MODULEPATH} ||= $modulepath unless ($modulepath =~ /^\@\@/);
19             }
20              
21 1     1   7 use strict;
  1         2  
  1         33  
22 1     1   10 use vars qw($VERSION $AUTOLOAD);
  1         2  
  1         64  
23              
24 1     1   5 use Carp;
  1         2  
  1         44  
25 1     1   5729 use IO::File;
  1         10090  
  1         1013  
26              
27             $VERSION = 1.3;
28              
29             my $modulecmd = $ENV{'PERL_MODULECMD'} || 'modulecmd';
30              
31             sub import {
32 1     1   22 my @args = @_;
33 1         1 shift @args;
34              
35             # import just dispatches commands to _modulecmd
36              
37 1         42 foreach my $arg (@args) {
38 0 0       0 if (ref ($arg) eq "HASH") {
39 0         0 my %hash = %{$arg};
  0         0  
40 0         0 foreach my $key (keys %hash) {
41 0         0 my $val = $hash{$key};
42 0 0       0 if (ref ($val) eq "ARRAY") {
43 0         0 _modulecmd ($key, $_) for @{$val};
  0         0  
44             } else {
45 0         0 _modulecmd ($key, $val);
46             }
47             }
48             } else {
49 0         0 _modulecmd ('load', $arg);
50             }
51             }
52             }
53              
54             sub AUTOLOAD {
55 1     1   14 my @modules = @_;
56              
57             # AUTOLOAD, like import, calls _modulecmd with the requested function
58              
59 1         2 my $fun = $AUTOLOAD;
60 1         8 $fun =~ s/^.*:://;
61              
62 1         7 _modulecmd ($fun, $_) for @modules;
63             }
64              
65             sub _indent {
66 1     1   4 my ($str) = @_;
67              
68 1         3 $str =~ s/\n$//;
69 1         3 $str =~ s/\n/\n -> /g;
70 1         4 $str = " -> $str\n";
71              
72 1         456 return ($str);
73             }
74              
75             sub _modulecmd {
76 1     1   2 my ($fun, $module) = @_;
77              
78             # here's where the actual work gets done. we call modulecmd and
79             # capture its standard output and standard error. we used to use
80             # IPC::Open3, but switched to temp files to resolve a potential hang
81             # on MS Windows 7 and up.
82              
83 1         4 my @cmd = ($modulecmd, "perl", $fun, $module);
84              
85             # 1. save stdout and stderr and redirect them to (unlinked) temp
86             # files
87              
88 1         15 my $SAVE_OUT = IO::File->new (">&" . STDOUT->fileno);
89 1         86 my $SAVE_ERR = IO::File->new (">&" . STDERR->fileno);
90              
91 1         294 my $OUT = IO::File->new_tmpfile;
92 1         115 my $ERR = IO::File->new_tmpfile;
93              
94 1         9 STDOUT->fdopen ($OUT, "w");
95 1         89 STDERR->fdopen ($ERR, "w");
96              
97 1         58 STDOUT->autoflush (1);
98 1         52 STDERR->autoflush (1);
99              
100             # 2. call modulecmd
101              
102 1         3057 my $retcode = system (@cmd);
103 1         57 my $syserr = $!;
104              
105             # 3. read the output from the temp files and restore stdout and
106             # stderr
107              
108 1         50 STDOUT->fdopen ($SAVE_OUT, "w");
109 1         1022 STDERR->fdopen ($SAVE_ERR, "w");
110              
111 1         84 $OUT->seek (0, 0);
112 1         17 $ERR->seek (0, 0);
113              
114 1         8 my $out;
115             my $err;
116 0         0 my $buf;
117              
118 1         100 $out .= $buf while read $OUT, $buf, 1024;
119 1         25 $err .= $buf while read $ERR, $buf, 1024;
120              
121 1         6 undef $OUT;
122 1         83 undef $ERR;
123              
124             # ok, how did we do?
125              
126 1 50 33     55 if ($err || $retcode) {
127 1         2 my $croak = 0;
128              
129             # attempt to guess whether the stderr output is a real error
130             # generated by modulecmd, or just an informational message output
131             # by the module itself. error messages from modulecmd (like
132             # "Couldn't find modulefile ... in MODULEPATH") fall into two
133             # categories: they either (a) start with "ERROR:", or (b) start
134             # and end with a row of dashes, and contain the message shown
135             # below. (note that "occurred" is misspelled as "occured" in the
136             # modulecmd source.)
137              
138 1   33     18 my $error_from_modulecmd =
139             (($err =~ /^ERROR:/) or
140             ($err =~ /^-----/ and $err =~ /-----\s*$/ and
141             $err =~ /An error occur*ed while processing your module command/));
142              
143 1 50       7 $croak = 1
144             if $error_from_modulecmd;
145              
146             # now check for failure from the system() call. start by checking
147             # for a non-zero return code, which works on some versions of
148             # MS Windows and most other systems.
149             #
150             # on MS Windows, shell commands are always wrapped with cmd.exe,
151             # and some versions of cmd.exe will always exit with a zero return
152             # code. Windows 7 seems to pass through the return code of the
153             # last command, but Windows XP and earlier can be problematic.
154             #
155             # modulecmd itself will hardly ever exit with a non-zero return
156             # code. however, there are two cases where it will: (a) invalid
157             # syntax, like "modulecmd no-such-shell list"; and (b) "modulecmd
158             # perl load /no/such/directory". in these cases, we hopefully
159             # already determined (using the pattern above) that this is an
160             # error message from modulecmd. if not, we assume it's a message
161             # about a failure to call modulecmd in the first place.
162              
163 1 50       4 if ($retcode) {
164 1         3 $croak = 1;
165              
166 1 50       5 unless ($error_from_modulecmd) {
167 1   33     21 croak
168             ("Unable to execute '@cmd':\n" .
169             _indent ($err || $syserr) .
170             "Error loading module $module");
171             }
172             }
173              
174             # now, if $croak is set, it's a fatal error, so croak on it.
175             # otherwise, issue a warning, but only if -w is in effect.
176              
177 0 0         if ($croak) {
178 0           croak
179             ("Errors from '@cmd':\n" .
180             _indent ($err) .
181             "Error loading module $module");
182             } else {
183 0 0         carp
184             ("Messages from '@cmd':\n" .
185             _indent ($err) .
186             "Possible error loading module $module")
187             if $^W;
188             }
189             }
190              
191             # if we got here, then the command didn't fail. if it did generate
192             # output, then we have something to eval.
193              
194 0 0         if ($out) {
195              
196             # what if we try to eval something that's not valid perl? in this
197             # case, eval will die, with a message indicating what went wrong.
198             # we want to catch this and nicely print out the error.
199              
200 0           eval $out;
201              
202 0 0         croak
203             ("'@cmd' generated output:\n" .
204             _indent ($out) .
205             "Error evaluating:\n" .
206             _indent ($@) .
207             "Error loading module $module")
208             if $@;
209             }
210             }
211              
212             1;
213              
214             __END__