Faster File::Compare
[p5sagit/p5-mst-13.2.git] / lib / AutoSplit.pm
1 package AutoSplit;
2
3 require 5.000;
4 require Exporter;
5
6 use Config;
7 use Carp;
8
9 @ISA = qw(Exporter);
10 @EXPORT = qw(&autosplit &autosplit_lib_modules);
11 @EXPORT_OK = qw($Verbose $Keep $Maxlen $CheckForAutoloader $CheckModTime);
12
13 =head1 NAME
14
15 AutoSplit - split a package for autoloading
16
17 =head1 SYNOPSIS
18
19  perl -e 'use AutoSplit; autosplit_lib_modules(@ARGV)' ...
20
21  use AutoSplit; autosplit($file, $dir, $keep, $check, $modtime);
22
23 for perl versions 5.002 and later:
24
25  perl -MAutoSplit -e 'autosplit($ARGV[0], $ARGV[1], $k, $chk, $modtime)' ...
26
27 =head1 DESCRIPTION
28
29 This function will split up your program into files that the AutoLoader
30 module can handle. It is used by both the standard perl libraries and by
31 the MakeMaker utility, to automatically configure libraries for autoloading.
32
33 The C<autosplit> interface splits the specified file into a hierarchy 
34 rooted at the directory C<$dir>. It creates directories as needed to reflect
35 class hierarchy, and creates the file F<autosplit.ix>. This file acts as
36 both forward declaration of all package routines, and as timestamp for the
37 last update of the hierarchy.
38
39 The remaining three arguments to C<autosplit> govern other options to the
40 autosplitter. If the third argument, I<$keep>, is false, then any pre-existing
41 C<.al> files in the autoload directory are removed if they are no longer
42 part of the module (obsoleted functions). The fourth argument, I<$check>,
43 instructs C<autosplit> to check the module currently being split to ensure
44 that it does include a C<use> specification for the AutoLoader module, and
45 skips the module if AutoLoader is not detected. Lastly, the I<$modtime>
46 argument specifies that C<autosplit> is to check the modification time of the
47 module against that of the C<autosplit.ix> file, and only split the module
48 if it is newer.
49
50 Typical use of AutoSplit in the perl MakeMaker utility is via the command-line
51 with:
52
53  perl -e 'use AutoSplit; autosplit($ARGV[0], $ARGV[1], 0, 1, 1)'
54
55 Defined as a Make macro, it is invoked with file and directory arguments;
56 C<autosplit> will split the specified file into the specified directory and
57 delete obsolete C<.al> files, after checking first that the module does use
58 the AutoLoader, and ensuring that the module is not already currently split
59 in its current form (the modtime test).
60
61 The C<autosplit_lib_modules> form is used in the building of perl. It takes
62 as input a list of files (modules) that are assumed to reside in a directory
63 B<lib> relative to the current directory. Each file is sent to the 
64 autosplitter one at a time, to be split into the directory B<lib/auto>.
65
66 In both usages of the autosplitter, only subroutines defined following the
67 perl special marker I<__END__> are split out into separate files. Some
68 routines may be placed prior to this marker to force their immediate loading
69 and parsing.
70
71 =head1 CAVEATS
72
73 Currently, C<AutoSplit> cannot handle multiple package specifications
74 within one file.
75
76 =head1 DIAGNOSTICS
77
78 C<AutoSplit> will inform the user if it is necessary to create the top-level
79 directory specified in the invocation. It is preferred that the script or
80 installation process that invokes C<AutoSplit> have created the full directory
81 path ahead of time. This warning may indicate that the module is being split
82 into an incorrect path.
83
84 C<AutoSplit> will warn the user of all subroutines whose name causes potential
85 file naming conflicts on machines with drastically limited (8 characters or
86 less) file name length. Since the subroutine name is used as the file name,
87 these warnings can aid in portability to such systems.
88
89 Warnings are issued and the file skipped if C<AutoSplit> cannot locate either
90 the I<__END__> marker or a "package Name;"-style specification.
91
92 C<AutoSplit> will also emit general diagnostics for inability to create
93 directories or files.
94
95 =cut
96
97 # for portability warn about names longer than $maxlen
98 $Maxlen  = 8;   # 8 for dos, 11 (14-".al") for SYSVR3
99 $Verbose = 1;   # 0=none, 1=minimal, 2=list .al files
100 $Keep    = 0;
101 $CheckForAutoloader = 1;
102 $CheckModTime = 1;
103
104 $IndexFile = "autosplit.ix";    # file also serves as timestamp
105 $maxflen = 255;
106 $maxflen = 14 if $Config{'d_flexfnam'} ne 'define';
107 $Is_VMS = ($^O eq 'VMS');
108
109
110 sub autosplit{
111     my($file, $autodir,  $k, $ckal, $ckmt) = @_;
112     # $file    - the perl source file to be split (after __END__)
113     # $autodir - the ".../auto" dir below which to write split subs
114     # Handle optional flags:
115     $keep = $Keep unless defined $k;
116     $ckal = $CheckForAutoloader unless defined $ckal;
117     $ckmt = $CheckModTime unless defined $ckmt;
118     autosplit_file($file, $autodir, $keep, $ckal, $ckmt);
119 }
120
121
122 # This function is used during perl building/installation
123 # ./miniperl -e 'use AutoSplit; autosplit_lib_modules(@ARGV)' ...
124
125 sub autosplit_lib_modules{
126     my(@modules) = @_; # list of Module names
127
128     foreach(@modules){
129         s#::#/#g;       # incase specified as ABC::XYZ
130         s|\\|/|g;               # bug in ksh OS/2
131         s#^lib/##; # incase specified as lib/*.pm
132         if ($Is_VMS && /[:>\]]/) { # may need to convert VMS-style filespecs
133             my ($dir,$name) = (/(.*])(.*)/);
134             $dir =~ s/.*lib[\.\]]//;
135             $dir =~ s#[\.\]]#/#g;
136             $_ = $dir . $name;
137         }
138         autosplit_file("lib/$_", "lib/auto", $Keep, $CheckForAutoloader, $CheckModTime);
139     }
140     0;
141 }
142
143
144 # private functions
145
146 sub autosplit_file{
147     my($filename, $autodir, $keep, $check_for_autoloader, $check_mod_time) = @_;
148     my(@names);
149
150     # where to write output files
151     $autodir = "lib/auto" unless $autodir;
152     if ($Is_VMS) {
153         ($autodir = VMS::Filespec::unixpath($autodir)) =~ s{/$}{};
154         $filename = VMS::Filespec::unixify($filename); # may have dirs
155     }
156     unless (-d $autodir){
157         local($", @p)="/";
158         foreach(split(/\//,$autodir)){
159             push(@p, $_);
160             next if -d "@p/";
161             mkdir("@p",0755) or die "AutoSplit unable to mkdir @p: $!";
162         }
163         # We should never need to create the auto dir here. installperl
164         # (or similar) should have done it. Expecting it to exist is a valuable
165         # sanity check against autosplitting into some random directory by mistake.
166         print "Warning: AutoSplit had to create top-level $autodir unexpectedly.\n";
167     }
168
169     # allow just a package name to be used
170     $filename .= ".pm" unless ($filename =~ m/\.pm$/);
171
172     open(IN, "<$filename") || die "AutoSplit: Can't open $filename: $!\n";
173     my($pm_mod_time) = (stat($filename))[9];
174     my($autoloader_seen) = 0;
175     my($in_pod) = 0;
176     while (<IN>) {
177         # Skip pod text.
178         $in_pod = 1 if /^=/;
179         $in_pod = 0 if /^=cut/;
180         next if ($in_pod || /^=cut/);
181
182         # record last package name seen
183         $package = $1 if (m/^\s*package\s+([\w:]+)\s*;/);
184         ++$autoloader_seen if m/^\s*(use|require)\s+AutoLoader\b/;
185         ++$autoloader_seen if m/\bISA\s*=.*\bAutoLoader\b/;
186         last if /^__END__/;
187     }
188     if ($check_for_autoloader && !$autoloader_seen){
189         print "AutoSplit skipped $filename: no AutoLoader used\n" if ($Verbose>=2);
190         return 0
191     }
192     $_ or die "Can't find __END__ in $filename\n";
193
194     $package or die "Can't find 'package Name;' in $filename\n";
195
196     my($modpname) = $package; $modpname =~ s#::#/#g;
197     my($al_idx_file) = "$autodir/$modpname/$IndexFile";
198
199     die "Package $package does not match filename $filename"
200             unless ($filename =~ m/$modpname.pm$/ or
201                     ($^O eq "msdos") or
202                     $Is_VMS && $filename =~ m/$modpname.pm/i);
203
204     if ($check_mod_time){
205         my($al_ts_time) = (stat("$al_idx_file"))[9] || 1;
206         if ($al_ts_time >= $pm_mod_time){
207             print "AutoSplit skipped ($al_idx_file newer that $filename)\n"
208                 if ($Verbose >= 2);
209             return undef;       # one undef, not a list
210         }
211     }
212
213     my($from) = ($Verbose>=2) ? "$filename => " : "";
214     print "AutoSplitting $package ($from$autodir/$modpname)\n"
215         if $Verbose;
216
217     unless (-d "$autodir/$modpname"){
218         local($", @p)="/";
219         foreach(split(/\//,"$autodir/$modpname")){
220             push(@p, $_);
221             next if -d "@p/";
222             mkdir("@p",0777) or die "AutoSplit unable to mkdir @p: $!";
223         }
224     }
225
226     # We must try to deal with some SVR3 systems with a limit of 14
227     # characters for file names. Sadly we *cannot* simply truncate all
228     # file names to 14 characters on these systems because we *must*
229     # create filenames which exactly match the names used by AutoLoader.pm.
230     # This is a problem because some systems silently truncate the file
231     # names while others treat long file names as an error.
232
233     # We do not yet deal with multiple packages within one file.
234     # Ideally both of these styles should work.
235     #
236     #   package NAME;
237     #   __END__
238     #   sub AAA { ... }
239     #   package NAME::option1;
240     #   sub BBB { ... }
241     #   package NAME::option2;
242     #   sub BBB { ... }
243     #
244     #   package NAME;
245     #   __END__
246     #   sub AAA { ... }
247     #   sub NAME::option1::BBB { ... }
248     #   sub NAME::option2::BBB { ... }
249     #
250     # For now both of these produce warnings.
251
252     open(OUT,">/dev/null") || open(OUT,">nla0:"); # avoid 'not opened' warning
253     my(@subnames, %proto);
254     my @cache = ();
255     my $caching = 1;
256     while (<IN>) {
257         next if /^=\w/ .. /^=cut/;
258         if (/^package ([\w:]+)\s*;/) {
259             warn "package $1; in AutoSplit section ignored. Not currently supported.";
260         }
261         if (/^sub\s+([\w:]+)(\s*\(.*?\))?/) {
262             print OUT "1;\n";
263             my $subname = $1;
264             $proto{$1} = $2 || '';
265             if ($subname =~ m/::/){
266                 warn "subs with package names not currently supported in AutoSplit section";
267             }
268             push(@subnames, $subname);
269             my($lname, $sname) = ($subname, substr($subname,0,$maxflen-3));
270             my($lpath) = "$autodir/$modpname/$lname.al";
271             my($spath) = "$autodir/$modpname/$sname.al";
272             unless(open(OUT, ">$lpath")){
273                 open(OUT, ">$spath") or die "Can't create $spath: $!\n";
274                 push(@names, $sname);
275                 print "  writing $spath (with truncated name)\n"
276                         if ($Verbose>=1);
277             }else{
278                 push(@names, $lname);
279                 print "  writing $lpath\n" if ($Verbose>=2);
280             }
281             print OUT "# NOTE: Derived from $filename.  ",
282                         "Changes made here will be lost.\n";
283             print OUT "package $package;\n\n";
284             print OUT @cache;
285             @cache = ();
286             $caching = 0;
287         }
288         if($caching) {
289             push(@cache, $_) if @cache || /\S/;
290         }
291         else {
292             print OUT $_;
293         }
294         if(/^}/) {
295             if($caching) {
296                 print OUT @cache;
297                 @cache = ();
298             }
299             print OUT "\n";
300             $caching = 1;
301         }
302     }
303     print OUT @cache,"1;\n";
304     close(OUT);
305     close(IN);
306
307     if (!$keep){  # don't keep any obsolete *.al files in the directory
308         my(%names);
309         @names{@names} = @names;
310         opendir(OUTDIR,"$autodir/$modpname");
311         foreach(sort readdir(OUTDIR)){
312             next unless /\.al$/;
313             my($subname) = m/(.*)\.al$/;
314             next if $names{substr($subname,0,$maxflen-3)};
315             my($file) = "$autodir/$modpname/$_";
316             print "  deleting $file\n" if ($Verbose>=2);
317             my($deleted,$thistime);  # catch all versions on VMS
318             do { $deleted += ($thistime = unlink $file) } while ($thistime);
319             carp "Unable to delete $file: $!" unless $deleted;
320         }
321         closedir(OUTDIR);
322     }
323
324     open(TS,">$al_idx_file") or
325         carp "AutoSplit: unable to create timestamp file ($al_idx_file): $!";
326     print TS "# Index created by AutoSplit for $filename (file acts as timestamp)\n";
327     print TS "package $package;\n";
328     print TS map("sub $_$proto{$_} ;\n", @subnames);
329     print TS "1;\n";
330     close(TS);
331
332     check_unique($package, $Maxlen, 1, @names);
333
334     @names;
335 }
336
337
338 sub check_unique{
339     my($module, $maxlen, $warn, @names) = @_;
340     my(%notuniq) = ();
341     my(%shorts)  = ();
342     my(@toolong) = grep(length > $maxlen, @names);
343
344     foreach(@toolong){
345         my($trunc) = substr($_,0,$maxlen);
346         $notuniq{$trunc}=1 if $shorts{$trunc};
347         $shorts{$trunc} = ($shorts{$trunc}) ? "$shorts{$trunc}, $_" : $_;
348     }
349     if (%notuniq && $warn){
350         print "$module: some names are not unique when truncated to $maxlen characters:\n";
351         foreach(keys %notuniq){
352             print " $shorts{$_} truncate to $_\n";
353         }
354     }
355     %notuniq;
356 }
357
358 1;
359 __END__
360
361 # test functions so AutoSplit.pm can be applied to itself:
362 sub test1{ "test 1\n"; }
363 sub test2{ "test 2\n"; }
364 sub test3{ "test 3\n"; }
365 sub test4{ "test 4\n"; }
366
367