ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Perl-LibExtractor/bin/perl-libextractor
Revision: 1.4
Committed: Wed Jan 25 21:27:54 2012 UTC (14 years, 8 months ago) by root
Branch: MAIN
CVS Tags: rel-1_0
Changes since 1.3: +9 -6 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #!/opt/bin/perl
2    
3     =head1 NAME
4    
5     perl-libextractor - determine perl library subsets for building distributions
6    
7     =head1 SYNOPSIS
8    
9     perl-libextractor ...
10    
11     General Options:
12    
13     -v verbose
14     --version display version
15     --bindir path perl executable target ("exe")
16     --dlldir path shared library target ("dll")
17     --scriptdir path executable script target ("bin")
18     --libdir path perl library target ("lib")
19     -I path prepend path to @INC
20     --no-packlists do not use packlists
21    
22     Selection:
23    
24     -Mmodule load and trace module
25     --script progname add executable script
26     --eval | -e str trace execution of perl string
27     --perl add perl interpreter itself
28     --core-support add core support
29     --unicore add unicore database
30     --core add perl core library
31     --glob glob add library files select by glob
32     --filter pat,... apply include/exclude patterns
33 root 1.3 --runtime-only remove files not needed for execution
34 root 1.1
35     Modes:
36    
37     --list list source and destination paths
38    
39     --dstlist list destination paths
40    
41     --srclist list source paths
42    
43     --copy path copy files to a target path
44     --strip strip .pl and .pm files
45     --cache-dir path cache directory to use
46     --binstrip "..." strip binaries and dlls
47    
48     =head1 DESCRIPTION
49    
50     This program can be used to extract a subset of your perl installation,
51     with the intention of building software distributions. Or in other words,
52     this module finds all files necessary to run a perl program (library
53     files, perl executable, scripts).
54    
55     The resulting set can then be displayed or copied to another directory,
56     while optionally stripping the perl sources to make them smaller.
57    
58     =head2 OPTIONS
59    
60     This manpage gives only rudimentary documentation for options that have an
61     equivalent in L<Perl::LibExtractor>, so look there for details.
62    
63     Options are not processed in order specified on the commandline, but in
64     multiple phases (e.g., C<-I> gets executed before any C<-M> option).
65    
66     =head3 GENERAL OPTIONS
67    
68     These options configure basic settings.
69    
70     =over 4
71    
72     =item C<--verbose>
73    
74     =item C<-v>
75    
76     Increases verbosity - highly recommended for interactive use.
77    
78     =item C<--version>
79    
80     Display the version of L<Perl::LibExtractor>.
81    
82     =item C<--exedir> I<path>
83    
84     Specifies the subdirectory for binary executables (the perl interpreter itself), instead of
85     the default F<exe/>.
86    
87     =item C<--dlldir> I<path>
88    
89     Specifies the subdirectory for shared libraries, instead of
90     the default F<dll/>.
91    
92     =item C<--bindir> I<path>
93    
94     Specifies the subdirectry for perl scripts, instead of
95     the default F<bin/>.
96    
97     =item C<--libdir> I<path>
98    
99     Specifies the subdirectory for perl library files, instead of
100     the default F<lib/>.
101    
102     =item C<-I> I<path>
103    
104     Prepends the given path to C<@INC> when searching perl library directories
105     (the last C<-I> option is prepended first).
106    
107 root 1.2 =item C<--no-packlists>
108    
109     Packlists allow to package all of a distribution, including resource files
110     not found through the normal tracing mechanism. This option disaables use
111     of packlists (normally highly recommended).
112    
113     Some especially broken perls (Debian GNU/Linux...) have missing files,
114     so this option doesn't work with them, at least not for any packages
115     distributed by debian (packages installed through CPAN or any other
116     non-dpkg-mechanism work fine).
117    
118 root 1.1 =back
119    
120     =head3 SELECTION
121    
122     These options specify and modify module selections. They are executed in
123     the order stated on the commandline, and when in doubt, you should use
124     them in the order documented here.
125    
126     =over 4
127    
128     =item C<-M>I<module>
129    
130     Load the named module and trace direct dependencies
131     (e.g. F<-MCarp>). Same as C<add_mod> in L<Perl::LibExtractor>.
132    
133     =item C<--script> I<progname>
134    
135     Compile the (installed) script I<progname> and trace dependencies
136     (e.g. F<corelist>). Same as C<add_bin> in L<Perl::LibExtractor>.
137    
138     =item C<--eval> I<string>
139    
140     =item C<-e str> I<string>
141    
142     Compile and execute the givne perl code, and trace dependencies (e.g. F<-e
143     "use AnyEvent; AnyEvent::detect">). Same as C<add_eval> in L<Perl::LibExtractor>.
144    
145     =item C<--perl>
146    
147     Adds the perl interpreter itself, including libperl if required to run
148     perl. Same as C<add_perl> in L<Perl::LibExtractor>.
149    
150     =item C<--core-support>
151    
152     Add all support files needed to support built-in features of perl (such
153     as C<ucfirst>), which is usually the minimum you should add from the core
154     library. Same as C<add_core_support> in L<Perl::LibExtractor>.
155    
156     =item C<--unicore>
157    
158     Add the whole unicore database, which is big, contains many, many files
159     and is usually not needed to run a program. Same as C<add_unicore> in
160     L<Perl::LibExtractor>.
161    
162     =item C<--core>
163    
164     Add the complete perl core library, which includes everything added
165     by F<--core-support> and F<--unicore>. Same as C<add_core> in
166     L<Perl::LibExtractor>.
167    
168 root 1.2 Some especially broken perls (Debian GNU/Linux...) have missing files, so
169     this option doesn't work with them.
170    
171 root 1.1 =item C<--glob glob>
172    
173     Add all files from the perl library directories thta match the given
174     extended glob pattern. Same as C<add_glob> in L<Perl::LibExtractor>, also
175     see there for the syntax of glob patterns.
176    
177     Example: add AnyEvent.pm and all AnyEvent::xxx modules installed.
178    
179     --glob Coro --glob "Coro::*"
180    
181     =item C<--filter pat,...>
182    
183     Apply a comma-separated series of extended glob patterns, prefixed by
184     C<+> (include) or C<-> (for exclude patterns). Same as C<filter> in
185     L<Perl::LibExtractor>, also see there for exact semantics and syntax.
186    
187     Example: remove all F<*.al> files in F<auto/POSIX>, except F<memset.al>.
188    
189     --filter "+/lib/auto/POSIX/memset.al,-/lib/auto/POSIX/*.al"
190    
191     =item C<--runtime-only>
192    
193 root 1.3 Remove all files not needed to run any scripts, such as debug info
194 root 1.1 files, link libraries, pod files and so on. Same as C<runtime_only> in
195     L<Perl::LibExtractor>.
196    
197     =back
198    
199     =head3 MODES
200    
201 root 1.4 These options select a specific work mode. Work modes might have specific
202 root 1.1 options to control them further.
203    
204     =over 4
205    
206     =item C<--list>
207    
208     Lists all selected files in two columns, first column is destination path,
209     second column is source path and first line is a header line.
210    
211     =item C<--dstlist>
212    
213     Same as C<--list>, but only list destination paths and has no header -
214     intended for scripts.
215    
216     =item C<--srclist>
217    
218     Same as C<--list>, but only list source paths and has no header -
219     intended for scripts.
220    
221     =item C<--copy> I<path>
222    
223     Copy all selected files to the respective position under the directory
224     I<path> - previous contents of the directory will be list.
225    
226     This mode has the following suboptions:
227    
228     =over 4
229    
230     =item C<--strip>
231    
232     Strip all C<.pm>, C<.pl>, C<.al> files and perl executables, which
233     mostly means removal of unecessary whitespace and documentation - see
234     L<Perl::Strip> which is used.
235    
236     =item C<--cache-dir> I<path>
237    
238 root 1.3 Specify the cache dir to use - the default is F<~/.cache/perlstrip> for
239     cached stripped perl files (compatible to the C<perlstrip> program).
240 root 1.1
241     =item C<--binstrip> I<"...">
242    
243     Use the specified program and arguments to strip executables, shared libraries and shared objects.
244    
245     This is only necessary when your programs were compiled with debugging
246     info, but can be used to specify extra treatment for all binary files.
247    
248     =back
249    
250     =back
251    
252     =head1 EXAMPLE
253    
254     #TODO#
255    
256     Let's first find out about the choice of paths for the subset. The
257     Deliantra client binary packages use L<Urlader> nowadays, and there it is
258     convenient to have F<perl> and any shared libraries directly in the root
259     of the distribution.
260    
261     The perl library files are put into a directory named F<pm>, simply
262     because it's shorter than F<lib>, and in the future, some files might go
263     into F<lib>.
264    
265     And finally, the F<deliantra> script itself is put into the perl library
266     directory, because it is not run directly - the installed client uses the
267     system fonts and other resources, while the binary package is supposed
268     to use the files packaged with it. To achieve this, a wrapper script is
269     created, called F<run>; which displays a splash screen and configures the
270     environment. A simplified version of it could look like this:
271    
272     @INC = ("pm", "."); # "." required by newer AutoLoader grrrr.
273     $ENV{PANGO_RC_FILE} = "pango.rc";
274     require "bin/deliantra";
275     exit 0;
276    
277     =head1 SEE ALSO
278    
279     L<App::Staticperl>, L<Perl::Squish>.
280    
281     =head1 AUTHOR
282    
283     Marc Lehmann <schmorp@schmorp.de>
284     http://software.schmorp.de/pkg/staticperl.html
285    
286     =cut
287    
288     use common::sense;
289    
290     use Getopt::Long;
291     use List::Util ();
292 root 1.3 use File::Path ();
293     use File::Basename ();
294     use File::Copy ();
295 root 1.1
296     use Perl::LibExtractor;
297     use Perl::Strip;
298    
299     our @INC;
300    
301     my $USE_PACKLISTS = 1;
302     my $VERBOSE;
303    
304     my $STRIP;
305     my $BINSTRIP;
306     my $CACHE;
307     my $CACHEDIR;
308    
309     my %DIR = qw(exe exe dll dll bin bin lib lib);
310    
311     $|=1;
312    
313     sub usage {
314     require Pod::Usage;
315    
316     Pod::Usage::pod2usage (-output => *STDOUT, -verbose => 1, -exitval => 1, -noperldoc => 1);
317     }
318    
319     @ARGV
320     or usage;
321    
322     Getopt::Long::Configure ("bundling", "no_auto_abbrev", "no_ignore_case");
323    
324     my $ex;
325     my $set;
326     my (@phase0, @phase1, @phase2);
327    
328     GetOptions
329 root 1.3 "verbose|v" => sub { ++$VERBOSE },
330 root 1.1 "version" => sub {
331     warn "This is perl-libextractor version $Perl::LibExtractor::VERSION\n";
332     },
333    
334     "exedir=s" => \$DIR{exe},
335     "dlldir=s" => \$DIR{dll},
336     "bindir=s" => \$DIR{bin},
337     "libdir=s" => \$DIR{lib},
338    
339     "I=s" => sub {
340     my $arg = $_[1];
341     unshift @phase0, sub { unshift @INC, $arg };
342     },
343     "no-packlists" => sub { $USE_PACKLISTS = 0 },
344    
345     "M=s" => sub {
346     my $arg = $_[1];
347     push @phase1, sub { $ex->add_mod ($arg) };
348     },
349     "script=s" => sub {
350     my $arg = $_[1];
351     push @phase1, sub { $ex->add_bin ($arg) };
352     },
353     "eval|e=s" => sub {
354     my $arg = $_[1];
355     push @phase1, sub { $ex->add_eval ($arg) };
356     },
357     "perl" => sub {
358     push @phase1, sub { $ex->add_perl };
359     },
360     "core-support" => sub {
361     push @phase1, sub { $ex->add_core_support };
362     },
363     "unicore" => sub {
364     push @phase1, sub { $ex->add_unicore };
365     },
366     "core" => sub {
367     push @phase1, sub { $ex->add_core };
368     },
369     "glob=s" => sub {
370     my $arg = $_[1];
371     push @phase1, sub { $ex->add_glob ($arg) };
372     },
373     "filter=s" => sub {
374     my $arg = $_[1];
375     push @phase1, sub { $ex->filter (split /,/, $arg) };
376     },
377     "runtime-only" => sub {
378     push @phase1, sub { $ex->runtime_only };
379     },
380    
381     "list" => sub {
382     push @phase2, \&mode_list;
383     },
384     "dstlist" => sub {
385     push @phase2, \&mode_dstlist;
386     },
387     "srclist" => sub {
388     push @phase2, \&mode_srclist;
389     },
390     "copy=s" => sub {
391     my $arg = $_[1];
392     push @phase2, sub { &mode_copy ($arg) };
393     },
394    
395     "strip" => \$STRIP,
396     "cache-dir=s" => \$CACHEDIR,
397     "binstrip=s" => \$BINSTRIP,
398    
399 root 1.3 or die "try $0 --help\n";
400 root 1.1
401     @phase2
402     or usage;
403    
404 root 1.3 # for strip_copy
405     my $STRIPPER = do {
406     my @cache;
407    
408     unless (defined $CACHEDIR) {
409     mkdir "$ENV{HOME}/.cache";
410     $CACHEDIR = "$ENV{HOME}/.cache/perlstrip";
411     }
412    
413     new Perl::Strip cache => $CACHEDIR, optimise_size => 1
414     };
415    
416     sub strip_copy($$) {
417     my ($src, $dst) = @_;
418    
419     my $text = do {
420     open my $fh, "<:perlio", $src
421     or die "$src: $!\n";
422    
423     local $/;
424     <$fh>
425     };
426    
427     printf "$dst: %d ", length $text if $VERBOSE >= 2;
428    
429     $text = $STRIPPER->strip ($text);
430    
431     printf "to %d bytes... ", length $text if $VERBOSE >= 2;
432    
433     open my $fh, ">:perlio", $dst
434     or die "$dst: $!\n";
435     length $text == syswrite $fh, $text
436     or die "$dst: $!\n";
437     close $fh;
438    
439     print "ok\n" if $VERBOSE >= 2;
440     }
441    
442 root 1.1 sub mode_list {
443     my @keys = sort keys %$set;
444     my $width = List::Util::max map length, @keys;
445    
446     printf "%-*.*s %s\n", $width, $width, "SRC", "DST";
447     for (@keys) {
448     printf "%-*.*s %s\n", $width, $width, $_, $set->{$_}[0];
449     }
450     }
451    
452     sub mode_dstlist {
453     print map "$_\n", sort keys %$set;
454     }
455    
456     sub mode_srclist {
457     print map "$set->{$_}[0]\n", sort keys %$set;
458     }
459    
460     sub mode_copy {
461 root 1.3 my $dst = shift;
462     print "deleting $dst.\n" if $VERBOSE;
463     File::Path::rmtree $dst, $VERBOSE >= 2, 0;
464    
465     print "populating $dst...\n" if $VERBOSE;
466    
467     my %mkdir;
468    
469     while (my ($path, $info) = each %$set) {
470     $path =~ m%^([^/]+)(.*)/([^/]+)$%
471     or die "$path: malformed destination path, please report.\n";
472    
473     my ($p1, $p2, $f) = ($1, $2, $3);
474    
475     my $dstdir = "$dst/$DIR{$p1}$p2";
476    
477     File::Path::mkpath $dstdir, $VERBOSE >= 2, 0777
478     unless $mkdir{$dstdir}++;
479    
480     my $src = $info->[0];
481    
482     if (
483     $STRIP
484     && ($f =~ /\.(?:pm|pl|ix|al)$/
485     || $p1 eq "bin")
486     ) {
487     strip_copy $src, "$dstdir/$f";
488    
489     } else {
490     File::Copy::cp $src, "$dstdir/$f"
491     or die "$src => $dstdir/$f: $!";
492    
493     if (
494 root 1.4 $f =~ /\.(?:bundle|dyld|so|dll|sl)$/ # TODO
495     || $p1 eq "exe"
496     || $p1 eq "dll"
497 root 1.3 ) {
498 root 1.4 if (defined $BINSTRIP) {
499     system "$BINSTRIP \Q$dstdir/$f";
500     }
501    
502     chmod 0777 & ~umask, "$dstdir/$f";
503 root 1.3 }
504     }
505     }
506 root 1.1 }
507    
508     $ex = new Perl::LibExtractor use_packlists => $USE_PACKLISTS;
509    
510     $_->() for @phase0;
511     $_->() for @phase1;
512    
513     $set = $ex->set;
514    
515     $_->() for @phase2;
516    
517     __END__
518    
519     print "$file... " if $VERBOSE;
520    
521     my $output = defined $OUTPUT ? $OUTPUT : $file;
522    
523     my $src = do {
524     open my $fh, "<:perlio", $file
525     or die "$file: $!\n";
526    
527     local $/;
528     <$fh>
529     };
530    
531     printf "%d ", length $src if $VERBOSE;
532    
533     $src = (new Perl::Strip @cache, optimise_size => $OPTIMISE_SIZE)->strip ($src);
534    
535     printf "to %d bytes... ", length $src if $VERBOSE;
536     print $output eq $file ? "writing... " : "saving as $output... " if $VERBOSE;
537    
538     open my $fh, ">:perlio", "$output~"
539     or die "$output~: $!\n";
540     length $src == syswrite $fh, $src
541     or die "$output~: $!\n";
542     close $fh;
543     rename "$output~", $output;
544    
545     print "ok\n" if $VERBOSE;
546     };
547    
548     if ($@) {
549     print STDERR "$@\n";
550     exit 2;
551     }
552     }
553    
554     __END__
555    
556    
557     die "cannot specify both --app and --perl\n"
558     if $PERL and defined $APP;
559    
560     # required for @INC loading, unfortunately
561     trace_module "PerlIO::scalar";
562    
563     #############################################################################
564     # apply include/exclude
565    
566     {
567     my %pmi;
568    
569     for (@incext) {
570     my ($inc, $glob) = @$_;
571    
572     my @match = grep /$glob/, keys %pm;
573    
574     if ($inc) {
575     # include
576     @pmi{@match} = delete @pm{@match};
577    
578     print "applying include $glob - protected ", (scalar @match), " files.\n"
579     if $VERBOSE >= 5;
580     } else {
581     # exclude
582     delete @pm{@match};
583    
584     print "applying exclude $glob - removed ", (scalar @match), " files.\n"
585     if $VERBOSE >= 5;
586     }
587     }
588    
589     my @pmi = keys %pmi;
590     @pm{@pmi} = delete @pmi{@pmi};
591     }
592    
593     #############################################################################
594     # scan for AutoLoader, static archives and other dependencies
595    
596     sub scan_al {
597     my ($auto, $autodir) = @_;
598    
599     my $ix = "$autodir/autosplit.ix";
600    
601     print "processing autoload index for '$auto'\n"
602     if $VERBOSE >= 6;
603    
604     $pm{"$auto/autosplit.ix"} = $ix;
605    
606     open my $fh, "<:perlio", $ix
607     or die "$ix: $!";
608    
609     my $package;
610    
611     while (<$fh>) {
612     if (/^\s*sub\s+ ([^[:space:];]+) \s* (?:\([^)]*\))? \s*;?\s*$/x) {
613     my $al = "auto/$package/$1.al";
614     my $inc = find_inc $al;
615    
616     defined $inc or die "$al: autoload file not found, but should be there.\n";
617    
618     $pm{$al} = $inc;
619     print "found autoload function '$al'\n"
620     if $VERBOSE >= 6;
621    
622     } elsif (/^\s*package\s+([^[:space:];]+)\s*;?\s*$/) {
623     ($package = $1) =~ s/::/\//g;
624     } elsif (/^\s*(?:#|1?\s*;?\s*$)/) {
625     # nop
626     } else {
627     warn "WARNING: $ix: unparsable line, please report: $_";
628     }
629     }
630     }
631    
632     for my $pm (keys %pm) {
633     if ($pm =~ /^(.*)\.pm$/) {
634     my $auto = "auto/$1";
635     my $autodir = find_inc $auto;
636    
637     if (defined $autodir && -d $autodir) {
638     # AutoLoader
639     scan_al $auto, $autodir
640     if -f "$autodir/autosplit.ix";
641    
642     # extralibs.ld
643     if (open my $fh, "<:perlio", "$autodir/extralibs.ld") {
644     print "found extralibs for $pm\n"
645     if $VERBOSE >= 6;
646    
647     local $/;
648     $extralibs .= " " . <$fh>;
649     }
650    
651     $pm =~ /([^\/]+).pm$/ or die "$pm: unable to match last component";
652    
653     my $base = $1;
654    
655     # static ext
656     if (-f "$autodir/$base$Config{_a}") {
657     print "found static archive for $pm\n"
658     if $VERBOSE >= 3;
659    
660     push @libs, "$autodir/$base$Config{_a}";
661     push @static_ext, $pm;
662     }
663    
664     # dynamic object
665     die "ERROR: found shared object - can't link statically ($_)\n"
666     if -f "$autodir/$base.$Config{dlext}";
667    
668     if ($PACKLIST && open my $fh, "<:perlio", "$autodir/.packlist") {
669     print "found .packlist for $pm\n"
670     if $VERBOSE >= 3;
671    
672     while (<$fh>) {
673     chomp;
674     s/ .*$//; # newer-style .packlists might contain key=value pairs
675    
676     # only include certain files (.al, .ix, .pm, .pl)
677     if (/\.(pm|pl|al|ix)$/) {
678     for my $inc (@INC) {
679     # in addition, we only add files that are below some @INC path
680     $inc =~ s/\/*$/\//;
681    
682     if ($inc eq substr $_, 0, length $inc) {
683     my $base = substr $_, length $inc;
684     $pm{$base} = $_;
685    
686     print "+ added .packlist dependency $base\n"
687     if $VERBOSE >= 3;
688     }
689    
690     last;
691     }
692     }
693     }
694     }
695     }
696     }
697     }
698    
699     #############################################################################
700    
701     print "processing bundle files (try more -v power if you get bored waiting here)...\n"
702     if $VERBOSE >= 1;
703    
704     my $data;
705     my @index;
706     my @order = sort {
707     length $a <=> length $b
708     or $a cmp $b
709     } keys %pm;
710    
711     # sorting by name - better compression, but needs more metadata
712     # sorting by length - faster lookup
713     # usually, the metadata overhead beats the loss through compression
714    
715     for my $pm (@order) {
716     my $path = $pm{$pm};
717    
718     128 > length $pm
719     or die "ERROR: $pm: path too long (only 128 octets supported)\n";
720    
721     my $src = ref $path
722     ? $$path
723     : do {
724     open my $pm, "<", $path
725     or die "$path: $!";
726    
727     local $/;
728    
729     <$pm>
730     };
731    
732     my $size = length $src;
733    
734     unless ($pmbin{$pm}) { # only do this unless the file is binary
735     if ($pm =~ /^auto\/POSIX\/[^\/]+\.al$/) {
736     if ($src =~ /^ unimpl \"/m) {
737     print "$pm: skipping (raises runtime error only).\n"
738     if $VERBOSE >= 3;
739     next;
740     }
741     }
742    
743     $src = cache +($STRIP eq "ppi" ? "$UNISTRIP,$OPTIMISE_SIZE" : undef), $src, sub {
744     if ($UNISTRIP && $pm =~ /^unicore\/.*\.pl$/) {
745     print "applying unicore stripping $pm\n"
746     if $VERBOSE >= 6;
747    
748     # special stripping for unicore swashes and properties
749     # much more could be done by going binary
750     $src =~ s{
751     (^return\ <<'END';\n) (.*?\n) (END(?:\n|\Z))
752     }{
753     my ($pre, $data, $post) = ($1, $2, $3);
754    
755     for ($data) {
756     s/^([0-9a-fA-F]+)\t([0-9a-fA-F]+)\t/sprintf "%X\t%X", hex $1, hex $2/gem
757     if $OPTIMISE_SIZE;
758    
759     # s{
760     # ^([0-9a-fA-F]+)\t([0-9a-fA-F]*)\t
761     # }{
762     # # ww - smaller filesize, UU - compress better
763     # pack "C0UU",
764     # hex $1,
765     # length $2 ? (hex $2) - (hex $1) : 0
766     # }gemx;
767    
768     s/#.*\n/\n/mg;
769     s/\s+\n/\n/mg;
770     }
771    
772     "$pre$data$post"
773     }smex;
774     }
775    
776     if ($STRIP =~ /ppi/i) {
777     require PPI;
778    
779     if (my $ppi = PPI::Document->new (\$src)) {
780     $ppi->prune ("PPI::Token::Comment");
781     $ppi->prune ("PPI::Token::Pod");
782    
783     # prune END stuff
784     for (my $last = $ppi->last_element; $last; ) {
785     my $prev = $last->previous_token;
786    
787     if ($last->isa (PPI::Token::Whitespace::)) {
788     $last->delete;
789     } elsif ($last->isa (PPI::Statement::End::)) {
790     $last->delete;
791     last;
792     } elsif ($last->isa (PPI::Token::Pod::)) {
793     $last->delete;
794     } else {
795     last;
796     }
797    
798     $last = $prev;
799     }
800    
801     # prune some but not all insignificant whitespace
802     for my $ws (@{ $ppi->find (PPI::Token::Whitespace::) }) {
803     my $prev = $ws->previous_token;
804     my $next = $ws->next_token;
805    
806     if (!$prev || !$next) {
807     $ws->delete;
808     } else {
809     if (
810     $next->isa (PPI::Token::Operator::) && $next->{content} =~ /^(?:,|=|!|!=|==|=>)$/ # no ., because of digits. == float
811     or $prev->isa (PPI::Token::Operator::) && $prev->{content} =~ /^(?:,|=|\.|!|!=|==|=>)$/
812     or $prev->isa (PPI::Token::Structure::)
813     or ($OPTIMISE_SIZE &&
814     ($prev->isa (PPI::Token::Word::)
815     && (PPI::Token::Symbol:: eq ref $next
816     || $next->isa (PPI::Structure::Block::)
817     || $next->isa (PPI::Structure::List::)
818     || $next->isa (PPI::Structure::Condition::)))
819     )
820     ) {
821     $ws->delete;
822     } elsif ($prev->isa (PPI::Token::Whitespace::)) {
823     $ws->{content} = ' ';
824     $prev->delete;
825     } else {
826     $ws->{content} = ' ';
827     }
828     }
829     }
830    
831     # prune whitespace around blocks
832     if ($OPTIMISE_SIZE) {
833     # these usually decrease size, but decrease compressability more
834     for my $struct (PPI::Structure::Block::, PPI::Structure::Condition::) {
835     for my $node (@{ $ppi->find ($struct) }) {
836     my $n1 = $node->first_token;
837     my $n2 = $n1->previous_token;
838     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
839     $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
840     my $n1 = $node->last_token;
841     my $n2 = $n1->next_token;
842     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
843     $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
844     }
845     }
846    
847     for my $node (@{ $ppi->find (PPI::Structure::List::) }) {
848     my $n1 = $node->first_token;
849     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
850     my $n1 = $node->last_token;
851     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
852     }
853     }
854    
855     # reformat qw() lists which often have lots of whitespace
856     for my $node (@{ $ppi->find (PPI::Token::QuoteLike::Words::) }) {
857     if ($node->{content} =~ /^qw(.)(.*)(.)$/s) {
858     my ($a, $qw, $b) = ($1, $2, $3);
859     $qw =~ s/^\s+//;
860     $qw =~ s/\s+$//;
861     $qw =~ s/\s+/ /g;
862     $node->{content} = "qw$a$qw$b";
863     }
864     }
865    
866     $src = $ppi->serialize;
867     } else {
868     warn "WARNING: $pm{$pm}: PPI failed to parse this file\n";
869     }
870     } elsif ($STRIP =~ /pod/i && $pm ne "Opcode.pm") { # opcode parses its own pod
871     require Pod::Strip;
872    
873     my $stripper = Pod::Strip->new;
874    
875     my $out;
876     $stripper->output_string (\$out);
877     $stripper->parse_string_document ($src)
878     or die;
879     $src = $out;
880     }
881    
882     if ($VERIFY && $pm =~ /\.pm$/ && $pm ne "Opcode.pm") {
883     if (open my $fh, "-|") {
884     <$fh>;
885     } else {
886     eval "#line 1 \"$pm\"\n$src" or warn "\n\n\n$pm\n\n$src\n$@\n\n\n";
887     exit 0;
888     }
889     }
890    
891     $src
892     };
893    
894     # if ($pm eq "Opcode.pm") {
895     # open my $fh, ">x" or die; print $fh $src;#d#
896     # exit 1;
897     # }
898     }
899    
900     print "adding $pm (original size $size, stored size ", length $src, ")\n"
901     if $VERBOSE >= 2;
902    
903     push @index, ((length $pm) << 25) | length $data;
904     $data .= $pm . $src;
905     }
906    
907     length $data < 2**25
908     or die "ERROR: bundle too large (only 32MB supported)\n";
909    
910     my $varpfx = "bundle_" . substr +(Digest::MD5::md5_hex $data), 0, 16;
911    
912     #############################################################################
913     # output
914    
915     print "generating $PREFIX.h... "
916     if $VERBOSE >= 1;
917    
918     {
919     open my $fh, ">", "$PREFIX.h"
920     or die "$PREFIX.h: $!\n";
921    
922     print $fh <<EOF;
923     /* do not edit, automatically created by mkstaticbundle */
924    
925     #include <EXTERN.h>
926     #include <perl.h>
927     #include <XSUB.h>
928    
929     /* public API */
930     EXTERN_C PerlInterpreter *staticperl;
931     EXTERN_C void staticperl_xs_init (pTHX);
932     EXTERN_C void staticperl_init (void);
933     EXTERN_C void staticperl_cleanup (void);
934    
935     EOF
936     }
937    
938     print "\n"
939     if $VERBOSE >= 1;
940    
941     #############################################################################
942     # output
943    
944     print "generating $PREFIX.c... "
945     if $VERBOSE >= 1;
946    
947     open my $fh, ">", "$PREFIX.c"
948     or die "$PREFIX.c: $!\n";
949    
950     print $fh <<EOF;
951     /* do not edit, automatically created by mkstaticbundle */
952    
953     #include "bundle.h"
954    
955     /* public API */
956     PerlInterpreter *staticperl;
957    
958     EOF
959    
960     #############################################################################
961     # bundle data
962    
963     my $count = @index;
964    
965     print $fh <<EOF;
966     #include "bundle.h"
967    
968     /* bundle data */
969    
970     static const U32 $varpfx\_count = $count;
971     static const U32 $varpfx\_index [$count + 1] = {
972     EOF
973    
974     my $col;
975     for (@index) {
976     printf $fh "0x%08x,", $_;
977     print $fh "\n" unless ++$col % 10;
978    
979     }
980     printf $fh "0x%08x\n};\n", (length $data);
981    
982     print $fh "static const char $varpfx\_data [] =\n";
983     dump_string $fh, $data;
984    
985     print $fh ";\n\n";
986    
987     #############################################################################
988     # bootstrap
989    
990     # boot file for staticperl
991     # this file will be eval'ed at initialisation time
992    
993     my $bootstrap = '
994     BEGIN {
995     package ' . $PACKAGE . ';
996    
997     PerlIO::scalar->bootstrap;
998    
999     @INC = sub {
1000     my $data = find "$_[1]"
1001     or return;
1002    
1003     $INC{$_[1]} = $_[1];
1004    
1005     open my $fh, "<", \$data;
1006     $fh
1007     };
1008     }
1009     ';
1010    
1011     $bootstrap .= "require '//boot';"
1012     if exists $pm{"//boot"};
1013    
1014     $bootstrap =~ s/\s+/ /g;
1015     $bootstrap =~ s/(\W) /$1/g;
1016     $bootstrap =~ s/ (\W)/$1/g;
1017    
1018     print $fh "const char bootstrap [] = ";
1019     dump_string $fh, $bootstrap;
1020     print $fh ";\n\n";
1021    
1022     print $fh <<EOF;
1023     /* search all bundles for the given file, using binary search */
1024     XS(find)
1025     {
1026     dXSARGS;
1027    
1028     if (items != 1)
1029     Perl_croak (aTHX_ "Usage: $PACKAGE\::find (\$path)");
1030    
1031     {
1032     STRLEN namelen;
1033     char *name = SvPV (ST (0), namelen);
1034     SV *res = 0;
1035    
1036     int l = 0, r = $varpfx\_count;
1037    
1038     while (l <= r)
1039     {
1040     int m = (l + r) >> 1;
1041     U32 idx = $varpfx\_index [m];
1042     int comp = namelen - (idx >> 25);
1043    
1044     if (!comp)
1045     {
1046     int ofs = idx & 0x1FFFFFFU;
1047     comp = memcmp (name, $varpfx\_data + ofs, namelen);
1048    
1049     if (!comp)
1050     {
1051     /* found */
1052     int ofs2 = $varpfx\_index [m + 1] & 0x1FFFFFFU;
1053    
1054     ofs += namelen;
1055     res = newSVpvn ($varpfx\_data + ofs, ofs2 - ofs);
1056     goto found;
1057     }
1058     }
1059    
1060     if (comp < 0)
1061     r = m - 1;
1062     else
1063     l = m + 1;
1064     }
1065    
1066     XSRETURN (0);
1067    
1068     found:
1069     ST (0) = res;
1070     sv_2mortal (ST (0));
1071     }
1072    
1073     XSRETURN (1);
1074     }
1075    
1076     /* list all files in the bundle */
1077     XS(list)
1078     {
1079     dXSARGS;
1080    
1081     if (items != 0)
1082     Perl_croak (aTHX_ "Usage: $PACKAGE\::list");
1083    
1084     {
1085     int i;
1086    
1087     EXTEND (SP, $varpfx\_count);
1088    
1089     for (i = 0; i < $varpfx\_count; ++i)
1090     {
1091     U32 idx = $varpfx\_index [i];
1092    
1093     PUSHs (newSVpvn ($varpfx\_data + (idx & 0x1FFFFFFU), idx >> 25));
1094     }
1095     }
1096    
1097     XSRETURN ($varpfx\_count);
1098     }
1099    
1100     EOF
1101    
1102     #############################################################################
1103     # xs_init
1104    
1105     print $fh <<EOF;
1106     void
1107     staticperl_xs_init (pTHX)
1108     {
1109     EOF
1110    
1111     @static_ext = ("DynaLoader", sort @static_ext);
1112    
1113     # prototypes
1114     for (@static_ext) {
1115     s/\.pm$//;
1116     (my $cname = $_) =~ s/\//__/g;
1117     print $fh " EXTERN_C void boot_$cname (pTHX_ CV* cv);\n";
1118     }
1119    
1120     print $fh <<EOF;
1121     char *file = __FILE__;
1122     dXSUB_SYS;
1123    
1124     newXSproto ("$PACKAGE\::find", find, file, "\$");
1125     newXSproto ("$PACKAGE\::list", list, file, "");
1126     EOF
1127    
1128     # calls
1129     for (@static_ext) {
1130     s/\.pm$//;
1131    
1132     (my $cname = $_) =~ s/\//__/g;
1133     (my $pname = $_) =~ s/\//::/g;
1134    
1135     my $bootstrap = $pname eq "DynaLoader" ? "boot" : "bootstrap";
1136    
1137     print $fh " newXS (\"$pname\::$bootstrap\", boot_$cname, file);\n";
1138     }
1139    
1140     print $fh <<EOF;
1141     Perl_av_create_and_unshift_one (&PL_preambleav, newSVpv (bootstrap, sizeof (bootstrap) - 1));
1142     }
1143     EOF
1144    
1145     #############################################################################
1146     # optional perl_init/perl_destroy
1147    
1148     if ($APP) {
1149     print $fh <<EOF;
1150    
1151     int
1152     main (int argc, char *argv [])
1153     {
1154     extern char **environ;
1155     int exitstatus;
1156    
1157     static char *args[] = {
1158     "staticperl",
1159     "-e",
1160     "0"
1161     };
1162    
1163     PERL_SYS_INIT3 (&argc, &argv, &environ);
1164     staticperl = perl_alloc ();
1165     perl_construct (staticperl);
1166    
1167     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1168    
1169     exitstatus = perl_parse (staticperl, staticperl_xs_init, sizeof (args) / sizeof (*args), args, environ);
1170     if (!exitstatus)
1171     perl_run (staticperl);
1172    
1173     exitstatus = perl_destruct (staticperl);
1174     perl_free (staticperl);
1175     PERL_SYS_TERM ();
1176    
1177     return exitstatus;
1178     }
1179     EOF
1180     } elsif ($PERL) {
1181     print $fh <<EOF;
1182    
1183     int
1184     main (int argc, char *argv [])
1185     {
1186     extern char **environ;
1187     int exitstatus;
1188    
1189     PERL_SYS_INIT3 (&argc, &argv, &environ);
1190     staticperl = perl_alloc ();
1191     perl_construct (staticperl);
1192    
1193     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1194    
1195     exitstatus = perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
1196     if (!exitstatus)
1197     perl_run (staticperl);
1198    
1199     exitstatus = perl_destruct (staticperl);
1200     perl_free (staticperl);
1201     PERL_SYS_TERM ();
1202    
1203     return exitstatus;
1204     }
1205     EOF
1206     } else {
1207     print $fh <<EOF;
1208    
1209     EXTERN_C void
1210     staticperl_init (void)
1211     {
1212     extern char **environ;
1213     int argc = sizeof (args) / sizeof (args [0]);
1214     char **argv = args;
1215    
1216     static char *args[] = {
1217     "staticperl",
1218     "-e",
1219     "0"
1220     };
1221    
1222     PERL_SYS_INIT3 (&argc, &argv, &environ);
1223     staticperl = perl_alloc ();
1224     perl_construct (staticperl);
1225     PL_origalen = 1;
1226     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1227     perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
1228    
1229     perl_run (staticperl);
1230     }
1231    
1232     EXTERN_C void
1233     staticperl_cleanup (void)
1234     {
1235     perl_destruct (staticperl);
1236     perl_free (staticperl);
1237     staticperl = 0;
1238     PERL_SYS_TERM ();
1239     }
1240     EOF
1241     }
1242    
1243     print -s "$PREFIX.c", " octets (", (length $data) , " data octets).\n\n"
1244     if $VERBOSE >= 1;
1245    
1246     #############################################################################
1247     # libs, cflags
1248    
1249     {
1250     print "generating $PREFIX.ccopts... "
1251     if $VERBOSE >= 1;
1252    
1253     my $str = "$Config{ccflags} $Config{optimize} $Config{cppflags} -I$Config{archlibexp}/CORE";
1254     $str =~ s/([\(\)])/\\$1/g;
1255    
1256     open my $fh, ">$PREFIX.ccopts"
1257     or die "$PREFIX.ccopts: $!";
1258     print $fh $str;
1259    
1260     print "$str\n\n"
1261     if $VERBOSE >= 1;
1262     }
1263    
1264     {
1265     print "generating $PREFIX.ldopts... ";
1266    
1267     my $str = $STATIC ? "-static " : "";
1268    
1269     $str .= "$Config{ccdlflags} $Config{ldflags} @libs $Config{archlibexp}/CORE/$Config{libperl} $Config{perllibs}";
1270    
1271     my %seen;
1272     $str .= " $_" for grep !$seen{$_}++, ($extralibs =~ /(\S+)/g);
1273    
1274     for (@staticlibs) {
1275     $str =~ s/(^|\s) (-l\Q$_\E) ($|\s)/$1-Wl,-Bstatic $2 -Wl,-Bdynamic$3/gx;
1276     }
1277    
1278     $str =~ s/([\(\)])/\\$1/g;
1279    
1280     open my $fh, ">$PREFIX.ldopts"
1281     or die "$PREFIX.ldopts: $!";
1282     print $fh $str;
1283    
1284     print "$str\n\n"
1285     if $VERBOSE >= 1;
1286     }
1287    
1288     if ($PERL or defined $APP) {
1289     $APP = "perl" unless defined $APP;
1290    
1291     print "building $APP...\n"
1292     if $VERBOSE >= 1;
1293    
1294     system "$Config{cc} \$(cat bundle.ccopts\) -o \Q$APP\E bundle.c \$(cat bundle.ldopts\)";
1295    
1296     unlink "$PREFIX.$_"
1297     for qw(ccopts ldopts c h);
1298    
1299     print "\n"
1300     if $VERBOSE >= 1;
1301     }
1302