line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#!/usr/bin/perl |
2
|
|
|
|
|
|
|
## Emacs: -*- tab-width: 4; -*- |
3
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
7687
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
68
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Module::Reload::Selective; |
7
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
5
|
use vars qw($VERSION); $VERSION = '1.02'; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
174
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=pod |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Module::Reload::Selective - Reload perl modules during development |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 SYNOPSIS |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Instead of: |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
use Foobar::MyModule; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Do this: |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use Module::Reload::Selective; |
25
|
|
|
|
|
|
|
&Module::Reload::Selective->reload(qw(Foobar::MyModule)); |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Or, if you need the "import" semantics of "use", do this: |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
use Foobar::MyModule (@ImportArgs); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
Do this: |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
use Module::Reload::Selective; |
34
|
|
|
|
|
|
|
Module::Reload::Selective->reload(qw(Foobar::MyModule)); |
35
|
|
|
|
|
|
|
import Foobar::MyModule (@ImportArgs); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
... then configure your server or other runtime environment settings |
39
|
|
|
|
|
|
|
to trigger Module::Reload::Selective to only kick in when you need. |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
For example: you could have it kick in only when the web server is |
42
|
|
|
|
|
|
|
running on a particular port number or particular (development) host. |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
=head1 OVERVIEW |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
Utility for module developers to selectively reload needed modules |
47
|
|
|
|
|
|
|
and/or conditionally augment @INC with additional, per-developer |
48
|
|
|
|
|
|
|
library directories, at development time based on environment |
49
|
|
|
|
|
|
|
variables. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Particularly helpful in conjunction with mod_perl applications where |
52
|
|
|
|
|
|
|
some or all application logic resides in separate Perl modules that |
53
|
|
|
|
|
|
|
would otherwise not get reloaded until the server restarts. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
Copyright (c) 2002, Chris Thorman. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
Released to the public under the terms of the Perl Artistic License. |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
=head1 DETAILS |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
This module defines a "reload" routine that scripts, CGI scripts, |
62
|
|
|
|
|
|
|
Embperl scripts, handlers, etc. can use to reload (re-require) a |
63
|
|
|
|
|
|
|
module or modules, optionally forcing the modules AND ANY MODULES THEY |
64
|
|
|
|
|
|
|
USE, recursively, to reload, even if already previously loaded and |
65
|
|
|
|
|
|
|
listed in %INC. |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
The reloading feature is helpful for when you're actively writing and |
68
|
|
|
|
|
|
|
debugging modules intended to be used with Apache and mod_perl (either |
69
|
|
|
|
|
|
|
used by Apache::Registry or HTML::Embperl script, or handlers, or |
70
|
|
|
|
|
|
|
other mechanisms) and want to ensure that your code changes get |
71
|
|
|
|
|
|
|
reloaded on every hit, even if the module had previously been loaded |
72
|
|
|
|
|
|
|
into the parent or child process. |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
In addition to the selective reloading feature, this module can also |
75
|
|
|
|
|
|
|
(optionally) dynamically prepend some additional paths to @INC to |
76
|
|
|
|
|
|
|
allow programmers to work on, test, and debug private development |
77
|
|
|
|
|
|
|
copies of modules in a private directory while other modules are |
78
|
|
|
|
|
|
|
loaded from a more stable, shared, or public, library directory. |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
The @INC-modifying feature is helpful even if you're only developing |
81
|
|
|
|
|
|
|
command-line perl scripts in an environment where there are multiple |
82
|
|
|
|
|
|
|
programmers and an individual programmer, for testing purposes, needs |
83
|
|
|
|
|
|
|
to optionally load some modules under development from his or her own |
84
|
|
|
|
|
|
|
private source directory in preference to the "standard" locations. |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
This is a common need when multiple Perl developers are working on the |
87
|
|
|
|
|
|
|
same Unix host. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
How this module differs from Module::Reload: |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=over 4 |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item * |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
Module::Reload reloads just files that it sees have changed on disk |
96
|
|
|
|
|
|
|
since the last reload, whereas this module conditionally reloads based |
97
|
|
|
|
|
|
|
on other conditions at runtime; this module also has other features of |
98
|
|
|
|
|
|
|
convenience to develoepers. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=back |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
How this module differs from Apache::StatINC: |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=over 4 |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=item * |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Reloads requested modules (recursively) regardless of modification |
109
|
|
|
|
|
|
|
date. |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=item * |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
Skips reloading any modules that have been previously loaded from |
114
|
|
|
|
|
|
|
lib/perl* (or other customizable list of dir name patterns), so you |
115
|
|
|
|
|
|
|
can only reload items outside the standard library locations, by |
116
|
|
|
|
|
|
|
default. |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=item * |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Allows dynamic overriding of @INC on a per-USER basis. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=item * |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
This module lacks StatINC's ability to disable symbol-redef warnings, |
125
|
|
|
|
|
|
|
so best not to reload modules with const subroutines... (sorry). |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item * |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Works outside of Apache as well as within it (not sure whether this is |
130
|
|
|
|
|
|
|
true of Apache::StatINC), so is testable from the command line, and |
131
|
|
|
|
|
|
|
even useful in a batch script context, if not for its reloading |
132
|
|
|
|
|
|
|
capabilities, then at least for its ability to override the search |
133
|
|
|
|
|
|
|
path on a per-USER basis, allowing the development and debugging of a |
134
|
|
|
|
|
|
|
private copy of a system-wide module or modules. |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=item * |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
Works fine from within individual pages or scripts; does not |
139
|
|
|
|
|
|
|
necessarily need to be loaded at server startup time. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item * |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Is a no-op (does not reload) unless certain environment variables |
144
|
|
|
|
|
|
|
and/or options are set, allowing you to leave calls to it in |
145
|
|
|
|
|
|
|
production code with negligible performance hit on non-debugging |
146
|
|
|
|
|
|
|
servers. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=back |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head1 DISCUSSION |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
To request that a module Foobar::MyModule, and any modules it calls, |
153
|
|
|
|
|
|
|
be reloaded, do this: |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
use Module::Reload::Selective; |
156
|
|
|
|
|
|
|
Module::Reload::Selective->reload(qw(Foobar::MyModule)); |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
This reloads the module, executing its BEGIN blocks, syntax-checking |
160
|
|
|
|
|
|
|
it, and recompiling any subroutines it has. |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Then, if you want to import any semantics from the module into the |
163
|
|
|
|
|
|
|
current namespace, you should directly "import" the module. |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
import Foobar::MyModule (@ImportArgs); |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
IMPORTANT: Under normal circumstances, reload will load the module |
168
|
|
|
|
|
|
|
normally with no difference from the usual behavior of "use" |
169
|
|
|
|
|
|
|
... i.e. files won't be reloaded if they already have been, and no |
170
|
|
|
|
|
|
|
special modifications to @INC will be applied. |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
BUT, if certain environment variables (see below) are set to non-false |
173
|
|
|
|
|
|
|
values, Module::Reload::Selective will force them and any modules THEY need, to |
174
|
|
|
|
|
|
|
be reloaded from their source file every time, also using temporary |
175
|
|
|
|
|
|
|
modifications to @INC. |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
The variables are: |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
$ENV{RLD} ## Useful for command-line testing/debugging |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
prompt> RLD=1 perl index.cgi |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
Just set this environment variable before invoking the script and the |
184
|
|
|
|
|
|
|
Reloader will be activated. |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
$ENV{DEBUGGING_SERVER} ## Set this in the server startup |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
At server startup, set the environment variable conditionally, only if |
189
|
|
|
|
|
|
|
you're starting up a private debugging server, say, on a different |
190
|
|
|
|
|
|
|
port. You could use something like this in a section in your |
191
|
|
|
|
|
|
|
httpd.conf, for example: |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
if (($My::HostName =~ /^dev/) && $Port == 8081) |
194
|
|
|
|
|
|
|
{ |
195
|
|
|
|
|
|
|
$User = 'upload'; |
196
|
|
|
|
|
|
|
$Group = 'upload'; |
197
|
|
|
|
|
|
|
print STDERR "Starting as user $User/$Group\n" if -t STDERR; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
push @PerlSetEnv, ['DEBUGGING_SERVER', 1]; |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
## Could also set other Module::Reload::Selective runtime options here. |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
=head1 RUNTIME OPTIONS |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
Runtime options are initialized by Module::Reload::Selective when it |
209
|
|
|
|
|
|
|
is first "use"d, and may be overridden individually later before |
210
|
|
|
|
|
|
|
calling "reload", by setting a few elements of the |
211
|
|
|
|
|
|
|
$Module::Reload::Selective::Options hash, like this: |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
$Module::Reload::Selective::Options->{SearchProgramDir} = 0; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
The available options, and their initial default values, are: |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
ReloadOnlyIfEnvVarsSet => 1, |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
## If 0, always reloads, regardless of environment var settings |
221
|
|
|
|
|
|
|
## described above. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
SearchProgramDir => 1, |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
## If 1, cur working dir of script, as determined from $0, will be |
226
|
|
|
|
|
|
|
## added to the search paths before reloading. |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
## This is very handy for keeping private local copies of modules |
229
|
|
|
|
|
|
|
## being tested in the same directory tree as the application that |
230
|
|
|
|
|
|
|
## uses them. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
SearchUserDir => 1, |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
## If 1, "user dir" as determined by the other "User" options |
235
|
|
|
|
|
|
|
## below, will added to the search paths, after ProgramDir. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
DontReloadIfPathContains => ['lib/perl'], |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
## List of strings that, if found in the loaded path of an already |
240
|
|
|
|
|
|
|
## loaded module, prevent that module from being re-loaded. By |
241
|
|
|
|
|
|
|
## specifying "lib/perl" (on Unix), no library modules installed |
242
|
|
|
|
|
|
|
## in the standard perl library locations will ever be reloaded. |
243
|
|
|
|
|
|
|
## Force reloading of those, too, by removing that entry, or add |
244
|
|
|
|
|
|
|
## additional strings to disable additional subdirectories, |
245
|
|
|
|
|
|
|
## perhaps ones of your own. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
FirstAdditionalPaths => [], |
248
|
|
|
|
|
|
|
LastAdditionalPaths => [], |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
## Lists; if non-empty, these specify additional paths to be |
251
|
|
|
|
|
|
|
## searched before or after any of the obove options, but in any |
252
|
|
|
|
|
|
|
## case always before any of the other locations normally in @INC. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
User => '', |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
## Name of user whose directory will be searched. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
DefaultUser => $ENV{RELOAD_USER} || $ENV{USER} || $ENV{REMOTE_USER}, |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
## Name of user whose directory will be searched if no User option |
261
|
|
|
|
|
|
|
## is specified to override it. If empty, no user name will be |
262
|
|
|
|
|
|
|
## used. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
UserDirTemplate => '/home/USER/src/lib', |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
## Path to search when looking for source modules in a User's |
267
|
|
|
|
|
|
|
## programming directory. If "USER" is in the path, it will be |
268
|
|
|
|
|
|
|
## substituted at runtime with the value of User or DefaultUser as |
269
|
|
|
|
|
|
|
## appropriate. The resulting directory path is only added to the |
270
|
|
|
|
|
|
|
## search paths if it actually exists. |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=head1 DEBUGGING & ANALYSIS OF WHAT GOT LOADED |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
For debugging purposes, Module::Reload::Selective creates these hash |
275
|
|
|
|
|
|
|
references, in the same format as %INC, that show what the last |
276
|
|
|
|
|
|
|
"reload" command did. You can examine these (e.g. with Data::Dumper) |
277
|
|
|
|
|
|
|
after calling reload if you want to be sure that reload did its job. |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCHashBefore} |
280
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCHashAfter} |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{NewlyLoaded} |
283
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{Reloaded} |
284
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{NotReloaded} |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{GotLoaded} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
NewlyLoaded -- modules that weren't loaded (from anywhere) prior to |
289
|
|
|
|
|
|
|
calling reload. |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Reloaded -- modules that previously appeared in %INC but got reloaded. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
NotReloaded -- modules that previously appeared in %INC but were not |
294
|
|
|
|
|
|
|
reloaded. |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
GotLoaded -- The union of Reloaded and NewlyLoaded -- i.e. anything |
297
|
|
|
|
|
|
|
that got loaded as a result of the "reload" command. |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
You can examine these by putting something like one of these |
300
|
|
|
|
|
|
|
statements in your code: |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
use Data::Dumper; |
303
|
|
|
|
|
|
|
print &Dumper($Module::Reload::Selective::Debug); |
304
|
|
|
|
|
|
|
print &Dumper($Module::Reload::Selective::Debug->{GotLoaded}); |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
For example, if you use HTML::Embperl, you could put the following |
307
|
|
|
|
|
|
|
line in your application: |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
[+ &Dumper($Module::Reload::Selective::Debug); +] |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
... and comment it out when done: |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
[# [+ &Dumper($Module::Reload::Selective::Debug); +] #] |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
Note: if you use "reload" to force a reload of all your modules into a |
316
|
|
|
|
|
|
|
virgin child process, the "NewlyLoaded" hash should be empty in a web |
317
|
|
|
|
|
|
|
server environment where the Web server has been properly configured |
318
|
|
|
|
|
|
|
to pre-load all necessary modules at server startup. You could use |
319
|
|
|
|
|
|
|
this side-effect as a way to test your server configuration to see if |
320
|
|
|
|
|
|
|
you've remembered to preload everything needed by your application at |
321
|
|
|
|
|
|
|
server startup; anything that shows up in NewlyLoaded is something |
322
|
|
|
|
|
|
|
you've forgotten to preload and you can fix that. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
To see what private version of @INC was used by "reload", have a look |
325
|
|
|
|
|
|
|
at this debugging variable: |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCArrayAfterModification} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
To see what time the last reload occurred, view this variable: |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{LastLoadTime} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
(This, along with the GotLoaded hash, will also help you reassure |
334
|
|
|
|
|
|
|
yourself that the things you wanted to reload really did get reloaded; |
335
|
|
|
|
|
|
|
if GotLoaded doesn't list your module, and/or LastLoadTime did not |
336
|
|
|
|
|
|
|
change, then something did not reload.) |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head1 WARNINGS |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
RELOADING IS RECURSIVE |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
If you reload module A that uses module B, module B will be reloaded, |
343
|
|
|
|
|
|
|
too. This allows you to reload all related modules at one time. But |
344
|
|
|
|
|
|
|
if you're not working on A, only on B, it is more efficient to just |
345
|
|
|
|
|
|
|
reload module B. Don't reload more than you need, in other words. |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
RELOADING CAN MESS WITH GLOBALS IN APACHE CHILD PROCESSES |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Note that the reloaded modules are loaded into the (child) process's |
351
|
|
|
|
|
|
|
global namespace and so will affect all applications served by the |
352
|
|
|
|
|
|
|
affected process... including any bugs you've introduced or modules |
353
|
|
|
|
|
|
|
that failed to compile. |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
So if using Module::Reload::Selective for Web application development, each |
356
|
|
|
|
|
|
|
programmer should be testing with his/her own private Apache server, |
357
|
|
|
|
|
|
|
(possibly running on a unique port). |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
This way when you force the reloading of a buggy version of a module, |
360
|
|
|
|
|
|
|
everyone else's runtime environment is not also screwed up. |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
WARNING: SYMBOL REDEFINITION WARNINGS |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
If the loaded module, or any module it reloads uses constant |
366
|
|
|
|
|
|
|
subroutines, as in constant.pm, you will get warnings every time it |
367
|
|
|
|
|
|
|
reloads. I tried to emulate a trick used by Doug MacEachern in |
368
|
|
|
|
|
|
|
Apache::StatINC to prevent this from happening, but in this version, I |
369
|
|
|
|
|
|
|
haven't been able to get that to work. Suggestions / fixes welcome. |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head1 THANKS |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Thanks to Joshua Pritikin for suggestions and the use of the |
374
|
|
|
|
|
|
|
Module::Reload namespace. |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=head1 INSTALLATION |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
Using CPAN module: |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
perl -MCPAN -e 'install Module::Reload::Selective' |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
Or manually: |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
tar xzvf Module-Reload-Sel*gz |
385
|
|
|
|
|
|
|
cd Module-Reload-Sel*-?.?? |
386
|
|
|
|
|
|
|
perl Makefile.PL |
387
|
|
|
|
|
|
|
make |
388
|
|
|
|
|
|
|
make test |
389
|
|
|
|
|
|
|
make install |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=head1 SEE ALSO |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
The Module::Reload::Selective home page: |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
http://christhorman.com/projects/perl/Module-Reload-Selective/ |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
Apache(3), mod_perl, http://perl.apache.org/src/contrib, |
398
|
|
|
|
|
|
|
Module::Reload, Apache::StatINC. |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
The implementation in Selective.pm. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
The perlmod manual page. |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=head1 AUTHOR |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
Chris Thorman |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
Copyright (c) 1995-2002 Chris Thorman. All rights reserved. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
411
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=cut |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
{}; ## Get emacs to indent correctly. Sigh. |
416
|
|
|
|
|
|
|
|
417
|
1
|
|
|
1
|
|
1213
|
use Data::Dumper; |
|
1
|
|
|
|
|
14830
|
|
|
1
|
|
|
|
|
222
|
|
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
BEGIN |
420
|
|
|
|
|
|
|
{ |
421
|
1
|
|
50
|
1
|
|
10
|
$Module::Reload::Selective::Debug ||= {}; |
422
|
|
|
|
|
|
|
|
423
|
1
|
|
50
|
|
|
7
|
$Module::Reload::Selective::Options ||= {}; |
424
|
|
|
|
|
|
|
|
425
|
1
|
|
50
|
|
|
7
|
$Module::Reload::Selective::Options->{ReloadOnlyIfEnvVarsSet} ||= 1; |
426
|
1
|
|
50
|
|
|
16
|
$Module::Reload::Selective::Options->{SearchProgramDir} ||= 1; |
427
|
1
|
|
50
|
|
|
6
|
$Module::Reload::Selective::Options->{SearchUserDir} ||= 1; |
428
|
|
|
|
|
|
|
|
429
|
1
|
|
50
|
|
|
7
|
$Module::Reload::Selective::Options->{DontReloadIfPathContains} ||= ['lib/perl']; |
430
|
|
|
|
|
|
|
|
431
|
1
|
|
50
|
|
|
7
|
$Module::Reload::Selective::Options->{FirstAdditionalPaths} ||= []; |
432
|
1
|
|
50
|
|
|
64
|
$Module::Reload::Selective::Options->{LastAdditionalPaths} ||= []; |
433
|
|
|
|
|
|
|
|
434
|
1
|
|
50
|
|
|
1603
|
$Module::Reload::Selective::Options->{User} ||= '', |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
50
|
|
|
|
|
435
|
|
|
|
|
|
|
$Module::Reload::Selective::Options->{DefaultUser} ||= $ENV{RELOAD_USER} || $ENV{USER} || $ENV{REMOTE_USER}, |
436
|
|
|
|
|
|
|
$Module::Reload::Selective::Options->{UserDirTemplate} ||= '/home/USER/src/lib', |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
} |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
sub import |
441
|
|
|
|
|
|
|
{ |
442
|
1
|
|
|
1
|
|
1963
|
my ($Class, @Args) = @_; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
### reload |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
### Can be called either procedurally or as a class method. Knows to |
449
|
|
|
|
|
|
|
### not reload the Module::Reload::Selective class itself. |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub reload |
452
|
|
|
|
|
|
|
{ |
453
|
0
|
|
|
0
|
0
|
|
my (@PackageNames) = @_; |
454
|
|
|
|
|
|
|
|
455
|
0
|
|
|
|
|
|
my $ReturnVal = undef; |
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
## This module doesn't reload itself. Sorry. Restart |
459
|
|
|
|
|
|
|
## your server for that. |
460
|
|
|
|
|
|
|
|
461
|
0
|
|
|
|
|
|
@PackageNames = (grep {$_ ne __PACKAGE__} @PackageNames); |
|
0
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
## Do nothing unless given at least one package name to reload. |
464
|
|
|
|
|
|
|
|
465
|
0
|
0
|
|
|
|
|
goto done unless @PackageNames; |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
## Initialize/empty out the debugging info |
468
|
|
|
|
|
|
|
|
469
|
0
|
|
|
|
|
|
$Module::Reload::Selective::Debug = {}; |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
## RELOAD MODE... kicks in if either of the two environment |
472
|
|
|
|
|
|
|
## variables is set or the ReloadOnlyIfEnvVarsSet option is turned |
473
|
|
|
|
|
|
|
## off. |
474
|
|
|
|
|
|
|
|
475
|
0
|
0
|
0
|
|
|
|
if ( |
|
|
|
0
|
|
|
|
|
476
|
|
|
|
|
|
|
$ENV{DEBUGGING_SERVER} || |
477
|
|
|
|
|
|
|
$ENV{RLD} || |
478
|
|
|
|
|
|
|
!$Module::Reload::Selective::Options->{ReloadOnlyIfEnvVarsSet} |
479
|
|
|
|
|
|
|
) |
480
|
|
|
|
|
|
|
{ |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
## FIRST MODIFY @INC TO HAVE SOME ADDITIONAL SEARCH DIRS |
483
|
|
|
|
|
|
|
## PREPENDED... |
484
|
|
|
|
|
|
|
|
485
|
0
|
|
|
|
|
|
my $ExtraSearchDirs = []; |
486
|
|
|
|
|
|
|
|
487
|
0
|
0
|
0
|
|
|
|
if ($Module::Reload::Selective::Options->{FirstAdditionalPaths} && |
|
0
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
@{$Module::Reload::Selective::Options->{FirstAdditionalPaths}}) |
489
|
|
|
|
|
|
|
{ |
490
|
0
|
|
|
|
|
|
push @$ExtraSearchDirs, @{$Module::Reload::Selective::Options->{FirstAdditionalPaths}}; |
|
0
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
0
|
0
|
|
|
|
|
if ($Module::Reload::Selective::Options->{SearchProgramDir}) |
494
|
|
|
|
|
|
|
{ |
495
|
0
|
|
|
|
|
|
(my $ProgramDir = $0) =~ s|(.*/).*|$1|; |
496
|
0
|
0
|
|
|
|
|
push @$ExtraSearchDirs, $ProgramDir if -d $ProgramDir; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
0
|
0
|
|
|
|
|
if ($Module::Reload::Selective::Options->{SearchUserDir}) |
500
|
|
|
|
|
|
|
{ |
501
|
|
|
|
|
|
|
my $User = ($Module::Reload::Selective::Options->{User } || |
502
|
0
|
|
0
|
|
|
|
$Module::Reload::Selective::Options->{DefaultUser}); |
503
|
|
|
|
|
|
|
|
504
|
0
|
|
|
|
|
|
my $UsersProgrammingDir = $Module::Reload::Selective::Options->{UserDirTemplate}; |
505
|
0
|
|
|
|
|
|
$UsersProgrammingDir =~ s/\bUSER\b/$User/g; |
506
|
|
|
|
|
|
|
|
507
|
0
|
0
|
0
|
|
|
|
push @$ExtraSearchDirs, $UsersProgrammingDir if $User && -d $UsersProgrammingDir; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
|
510
|
0
|
0
|
0
|
|
|
|
if ($Module::Reload::Selective::Options->{LastAdditionalPaths} && |
|
0
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
@{$Module::Reload::Selective::Options->{LastAdditionalPaths}}) |
512
|
|
|
|
|
|
|
{ |
513
|
0
|
|
|
|
|
|
push @$ExtraSearchDirs, @{$Module::Reload::Selective::Options->{LastAdditionalPaths}}; |
|
0
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
## Prepend the ExtraSearchDirs to a local copy of @INC so they |
517
|
|
|
|
|
|
|
## will get searched in order before any of the places in |
518
|
|
|
|
|
|
|
## @INC. |
519
|
|
|
|
|
|
|
|
520
|
0
|
|
|
|
|
|
local @INC = @INC; |
521
|
0
|
|
|
|
|
|
unshift @INC, @$ExtraSearchDirs; |
522
|
|
|
|
|
|
|
|
523
|
0
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCArrayAfterModification} = [@INC]; |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
## die &Dumper(\@INC, $ExtraSearchDirs, $Module::Reload::Selective::Options); |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
## THEN MODIFY %INC TO REMOVE ANY MENTION OF ITEMS THAT MIGHT |
528
|
|
|
|
|
|
|
## NEED TO GET RELOADED.... |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
## Before mucking with %INC, get a list of any installed perl |
531
|
|
|
|
|
|
|
## library modules that we don't want to muck with (any items |
532
|
|
|
|
|
|
|
## with "lib/perl" in the path).... or any other of a list of |
533
|
|
|
|
|
|
|
## path elements that should be disabled... |
534
|
|
|
|
|
|
|
|
535
|
0
|
|
|
|
|
|
my $DisabledPatterns = $Module::Reload::Selective::Options->{DontReloadIfPathContains}; |
536
|
|
|
|
|
|
|
|
537
|
0
|
|
|
|
|
|
my $DisabledItems = {}; |
538
|
0
|
|
|
|
|
|
foreach my $Pattern (@$DisabledPatterns) |
539
|
|
|
|
|
|
|
{ |
540
|
0
|
|
|
|
|
|
@$DisabledItems{grep {$INC{$_} =~ m|\Q$Pattern\E|} keys %INC} = undef; |
|
0
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
} |
542
|
0
|
|
|
|
|
|
@$DisabledItems{keys %$DisabledItems} = @INC{keys %$DisabledItems}; |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
## die &Dumper($DisabledItems); |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
## Empty out our private copy of %INC, so "require" doesn't |
547
|
|
|
|
|
|
|
## think any modules have yet been loaded. |
548
|
|
|
|
|
|
|
|
549
|
0
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCHashBefore} = {%INC}; |
550
|
0
|
|
|
|
|
|
local %INC = (); |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
## Restore the disabled items in %INC so we don't reload those. |
553
|
|
|
|
|
|
|
|
554
|
0
|
|
|
|
|
|
@INC{keys %$DisabledItems} = values %$DisabledItems; |
555
|
|
|
|
|
|
|
## die &Dumper(\%INC); |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
## THEN LOAD EACH OF THE REQUESTED MODULES... |
559
|
|
|
|
|
|
|
|
560
|
0
|
|
|
|
|
|
foreach my $PackageName (@PackageNames) |
561
|
|
|
|
|
|
|
{ |
562
|
|
|
|
|
|
|
|
563
|
0
|
|
|
|
|
|
(my $PackageRelPath = "$PackageName.pm") =~ s|::|/|g; |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
## Many thanks to Doug MacEachern / Apache::StatINC for |
566
|
|
|
|
|
|
|
## this attempt turning of warnings for const subroutine |
567
|
|
|
|
|
|
|
## redefinitions, but I can't seem to get it to work, so it's commented out. |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
## require Apache::Symbol; |
570
|
|
|
|
|
|
|
## my $Class = Apache::Symbol::file2class($PackageRelPath); |
571
|
|
|
|
|
|
|
## $Class->Apache::Symbol::undef_functions( undef, 1 ); |
572
|
|
|
|
|
|
|
|
573
|
0
|
|
|
|
|
|
require ($PackageRelPath); |
574
|
0
|
|
|
|
|
|
$ReturnVal = import $PackageName; |
575
|
|
|
|
|
|
|
} |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
## TAKE A SNAPSHOT OF THE NEW %INC FOR LATER ANALYSIS... |
578
|
|
|
|
|
|
|
|
579
|
0
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCHashAfter} = {%INC}; |
580
|
0
|
|
|
|
|
|
delete @{$Module::Reload::Selective::Debug->{INCHashAfter}}{keys %$DisabledItems}; |
|
0
|
|
|
|
|
|
|
581
|
0
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{LastLoadTime} = localtime().''; |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
## %INC and @INC WILL BE RESTORED HERE BY local %INC and local |
584
|
|
|
|
|
|
|
## @INC GOING OUT OF SCOPE... |
585
|
|
|
|
|
|
|
} |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
## REGULAR MODE: Do the equivlaent of a "use", except that |
588
|
|
|
|
|
|
|
## semantics won't be imported into the caller's namespace. |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
else |
591
|
|
|
|
|
|
|
{ |
592
|
0
|
|
|
|
|
|
foreach my $PackageName (@PackageNames) |
593
|
|
|
|
|
|
|
{ |
594
|
0
|
|
|
|
|
|
(my $PackageRelPath = "$PackageName.pm") =~ s|::|/|g; |
595
|
0
|
|
|
|
|
|
require ($PackageRelPath); |
596
|
0
|
|
|
|
|
|
$ReturnVal = import $PackageName; |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
0
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{Reload_Disabled_Because} = "No environment variables were set."; |
600
|
|
|
|
|
|
|
} |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
done: |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
## if %INC was messed with, we analyze the differences between the |
605
|
|
|
|
|
|
|
## before and after and set some derived hashes. |
606
|
|
|
|
|
|
|
|
607
|
0
|
0
|
0
|
|
|
|
if ($Module::Reload::Selective::Debug->{INCHashBefore} && |
608
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCHashAfter}) |
609
|
|
|
|
|
|
|
{ |
610
|
0
|
|
|
|
|
|
($Module::Reload::Selective::Debug->{NotReloaded}, |
611
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{NewlyLoaded}, |
612
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{Reloaded}) = |
613
|
|
|
|
|
|
|
CompareHashKeys($Module::Reload::Selective::Debug->{INCHashBefore}, |
614
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCHashAfter}); |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
## CompareHashKeys leaves the values undefined; for |
617
|
|
|
|
|
|
|
## convenience, we pick up the values from the Before and |
618
|
|
|
|
|
|
|
## After hashes. |
619
|
|
|
|
|
|
|
|
620
|
0
|
|
|
|
|
|
foreach ($Module::Reload::Selective::Debug->{NotReloaded}, |
621
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{NewlyLoaded}, |
622
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{Reloaded}) |
623
|
|
|
|
|
|
|
{ |
624
|
0
|
0
|
|
|
|
|
@{$_}{keys %$_} = (map {($Module::Reload::Selective::Debug->{INCHashAfter }->{$_} || |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{INCHashBefore}->{$_})} keys %$_); |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
## Finally make another debugging hash of anything that got |
629
|
|
|
|
|
|
|
## loaded for any reason, whether newly, or re-loaded. |
630
|
|
|
|
|
|
|
|
631
|
0
|
|
|
|
|
|
$Module::Reload::Selective::Debug->{GotLoaded} = {}; |
632
|
0
|
|
|
|
|
|
@{$Module::Reload::Selective::Debug->{GotLoaded}}{keys %{$Module::Reload::Selective::Debug->{NewlyLoaded}}} = values %{$Module::Reload::Selective::Debug->{NewlyLoaded}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
633
|
0
|
|
|
|
|
|
@{$Module::Reload::Selective::Debug->{GotLoaded}}{keys %{$Module::Reload::Selective::Debug->{Reloaded }}} = values %{$Module::Reload::Selective::Debug->{Reloaded }}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
## Copy entries for the items that changed into the non-local %INC hash. |
636
|
0
|
|
|
|
|
|
@INC{keys %{$Module::Reload::Selective::Debug->{GotLoaded}}} = values %{$Module::Reload::Selective::Debug->{GotLoaded}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
} |
639
|
|
|
|
|
|
|
|
640
|
0
|
|
|
|
|
|
return($ReturnVal); |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
######### Utility routines ########## |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
sub CompareHashKeys |
648
|
|
|
|
|
|
|
{ |
649
|
0
|
|
|
0
|
0
|
|
my ($Hash1, $Hash2) = @_; |
650
|
|
|
|
|
|
|
|
651
|
0
|
|
|
|
|
|
my $In1NotIn2 = {}; @$In1NotIn2{keys %$Hash1 } = undef; delete @$In1NotIn2{keys %$Hash2 }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
652
|
0
|
|
|
|
|
|
my $In2NotIn1 = {}; @$In2NotIn1{keys %$Hash2 } = undef; delete @$In2NotIn1{keys %$Hash1 }; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
653
|
0
|
|
|
|
|
|
my $Subset = {}; @$Subset {keys %$Hash1, keys %$Hash2} = undef; delete @$Subset {keys %$In1NotIn2, keys %$In2NotIn1}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
|
655
|
0
|
|
|
|
|
|
return($In1NotIn2, $In2NotIn1, $Subset); |
656
|
|
|
|
|
|
|
} |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
1; |