ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Perl-LibExtractor/bin/perl-libextractor
Revision: 1.3
Committed: Mon Jan 23 02:36:10 2012 UTC (14 years, 7 months ago) by root
Branch: MAIN
Changes since 1.2: +89 -22 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     These options select a specific work mode. Work mode smight have specific
202     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     (defined $BINSTRIP)
495     && ($f =~ /\.(?:bundle|dyld|so|dll|sl)$/ # TODO
496     || $p1 eq "exe"
497     || $p1 eq "dll")
498     ) {
499     system "$BINSTRIP \Q$dstdir/$f";
500     }
501     }
502     }
503 root 1.1 }
504    
505     $ex = new Perl::LibExtractor use_packlists => $USE_PACKLISTS;
506    
507     $_->() for @phase0;
508     $_->() for @phase1;
509    
510     $set = $ex->set;
511    
512     $_->() for @phase2;
513    
514     __END__
515    
516     print "$file... " if $VERBOSE;
517    
518     my $output = defined $OUTPUT ? $OUTPUT : $file;
519    
520     my $src = do {
521     open my $fh, "<:perlio", $file
522     or die "$file: $!\n";
523    
524     local $/;
525     <$fh>
526     };
527    
528     printf "%d ", length $src if $VERBOSE;
529    
530     $src = (new Perl::Strip @cache, optimise_size => $OPTIMISE_SIZE)->strip ($src);
531    
532     printf "to %d bytes... ", length $src if $VERBOSE;
533     print $output eq $file ? "writing... " : "saving as $output... " if $VERBOSE;
534    
535     open my $fh, ">:perlio", "$output~"
536     or die "$output~: $!\n";
537     length $src == syswrite $fh, $src
538     or die "$output~: $!\n";
539     close $fh;
540     rename "$output~", $output;
541    
542     print "ok\n" if $VERBOSE;
543     };
544    
545     if ($@) {
546     print STDERR "$@\n";
547     exit 2;
548     }
549     }
550    
551     __END__
552    
553    
554     die "cannot specify both --app and --perl\n"
555     if $PERL and defined $APP;
556    
557     # required for @INC loading, unfortunately
558     trace_module "PerlIO::scalar";
559    
560     #############################################################################
561     # apply include/exclude
562    
563     {
564     my %pmi;
565    
566     for (@incext) {
567     my ($inc, $glob) = @$_;
568    
569     my @match = grep /$glob/, keys %pm;
570    
571     if ($inc) {
572     # include
573     @pmi{@match} = delete @pm{@match};
574    
575     print "applying include $glob - protected ", (scalar @match), " files.\n"
576     if $VERBOSE >= 5;
577     } else {
578     # exclude
579     delete @pm{@match};
580    
581     print "applying exclude $glob - removed ", (scalar @match), " files.\n"
582     if $VERBOSE >= 5;
583     }
584     }
585    
586     my @pmi = keys %pmi;
587     @pm{@pmi} = delete @pmi{@pmi};
588     }
589    
590     #############################################################################
591     # scan for AutoLoader, static archives and other dependencies
592    
593     sub scan_al {
594     my ($auto, $autodir) = @_;
595    
596     my $ix = "$autodir/autosplit.ix";
597    
598     print "processing autoload index for '$auto'\n"
599     if $VERBOSE >= 6;
600    
601     $pm{"$auto/autosplit.ix"} = $ix;
602    
603     open my $fh, "<:perlio", $ix
604     or die "$ix: $!";
605    
606     my $package;
607    
608     while (<$fh>) {
609     if (/^\s*sub\s+ ([^[:space:];]+) \s* (?:\([^)]*\))? \s*;?\s*$/x) {
610     my $al = "auto/$package/$1.al";
611     my $inc = find_inc $al;
612    
613     defined $inc or die "$al: autoload file not found, but should be there.\n";
614    
615     $pm{$al} = $inc;
616     print "found autoload function '$al'\n"
617     if $VERBOSE >= 6;
618    
619     } elsif (/^\s*package\s+([^[:space:];]+)\s*;?\s*$/) {
620     ($package = $1) =~ s/::/\//g;
621     } elsif (/^\s*(?:#|1?\s*;?\s*$)/) {
622     # nop
623     } else {
624     warn "WARNING: $ix: unparsable line, please report: $_";
625     }
626     }
627     }
628    
629     for my $pm (keys %pm) {
630     if ($pm =~ /^(.*)\.pm$/) {
631     my $auto = "auto/$1";
632     my $autodir = find_inc $auto;
633    
634     if (defined $autodir && -d $autodir) {
635     # AutoLoader
636     scan_al $auto, $autodir
637     if -f "$autodir/autosplit.ix";
638    
639     # extralibs.ld
640     if (open my $fh, "<:perlio", "$autodir/extralibs.ld") {
641     print "found extralibs for $pm\n"
642     if $VERBOSE >= 6;
643    
644     local $/;
645     $extralibs .= " " . <$fh>;
646     }
647    
648     $pm =~ /([^\/]+).pm$/ or die "$pm: unable to match last component";
649    
650     my $base = $1;
651    
652     # static ext
653     if (-f "$autodir/$base$Config{_a}") {
654     print "found static archive for $pm\n"
655     if $VERBOSE >= 3;
656    
657     push @libs, "$autodir/$base$Config{_a}";
658     push @static_ext, $pm;
659     }
660    
661     # dynamic object
662     die "ERROR: found shared object - can't link statically ($_)\n"
663     if -f "$autodir/$base.$Config{dlext}";
664    
665     if ($PACKLIST && open my $fh, "<:perlio", "$autodir/.packlist") {
666     print "found .packlist for $pm\n"
667     if $VERBOSE >= 3;
668    
669     while (<$fh>) {
670     chomp;
671     s/ .*$//; # newer-style .packlists might contain key=value pairs
672    
673     # only include certain files (.al, .ix, .pm, .pl)
674     if (/\.(pm|pl|al|ix)$/) {
675     for my $inc (@INC) {
676     # in addition, we only add files that are below some @INC path
677     $inc =~ s/\/*$/\//;
678    
679     if ($inc eq substr $_, 0, length $inc) {
680     my $base = substr $_, length $inc;
681     $pm{$base} = $_;
682    
683     print "+ added .packlist dependency $base\n"
684     if $VERBOSE >= 3;
685     }
686    
687     last;
688     }
689     }
690     }
691     }
692     }
693     }
694     }
695    
696     #############################################################################
697    
698     print "processing bundle files (try more -v power if you get bored waiting here)...\n"
699     if $VERBOSE >= 1;
700    
701     my $data;
702     my @index;
703     my @order = sort {
704     length $a <=> length $b
705     or $a cmp $b
706     } keys %pm;
707    
708     # sorting by name - better compression, but needs more metadata
709     # sorting by length - faster lookup
710     # usually, the metadata overhead beats the loss through compression
711    
712     for my $pm (@order) {
713     my $path = $pm{$pm};
714    
715     128 > length $pm
716     or die "ERROR: $pm: path too long (only 128 octets supported)\n";
717    
718     my $src = ref $path
719     ? $$path
720     : do {
721     open my $pm, "<", $path
722     or die "$path: $!";
723    
724     local $/;
725    
726     <$pm>
727     };
728    
729     my $size = length $src;
730    
731     unless ($pmbin{$pm}) { # only do this unless the file is binary
732     if ($pm =~ /^auto\/POSIX\/[^\/]+\.al$/) {
733     if ($src =~ /^ unimpl \"/m) {
734     print "$pm: skipping (raises runtime error only).\n"
735     if $VERBOSE >= 3;
736     next;
737     }
738     }
739    
740     $src = cache +($STRIP eq "ppi" ? "$UNISTRIP,$OPTIMISE_SIZE" : undef), $src, sub {
741     if ($UNISTRIP && $pm =~ /^unicore\/.*\.pl$/) {
742     print "applying unicore stripping $pm\n"
743     if $VERBOSE >= 6;
744    
745     # special stripping for unicore swashes and properties
746     # much more could be done by going binary
747     $src =~ s{
748     (^return\ <<'END';\n) (.*?\n) (END(?:\n|\Z))
749     }{
750     my ($pre, $data, $post) = ($1, $2, $3);
751    
752     for ($data) {
753     s/^([0-9a-fA-F]+)\t([0-9a-fA-F]+)\t/sprintf "%X\t%X", hex $1, hex $2/gem
754     if $OPTIMISE_SIZE;
755    
756     # s{
757     # ^([0-9a-fA-F]+)\t([0-9a-fA-F]*)\t
758     # }{
759     # # ww - smaller filesize, UU - compress better
760     # pack "C0UU",
761     # hex $1,
762     # length $2 ? (hex $2) - (hex $1) : 0
763     # }gemx;
764    
765     s/#.*\n/\n/mg;
766     s/\s+\n/\n/mg;
767     }
768    
769     "$pre$data$post"
770     }smex;
771     }
772    
773     if ($STRIP =~ /ppi/i) {
774     require PPI;
775    
776     if (my $ppi = PPI::Document->new (\$src)) {
777     $ppi->prune ("PPI::Token::Comment");
778     $ppi->prune ("PPI::Token::Pod");
779    
780     # prune END stuff
781     for (my $last = $ppi->last_element; $last; ) {
782     my $prev = $last->previous_token;
783    
784     if ($last->isa (PPI::Token::Whitespace::)) {
785     $last->delete;
786     } elsif ($last->isa (PPI::Statement::End::)) {
787     $last->delete;
788     last;
789     } elsif ($last->isa (PPI::Token::Pod::)) {
790     $last->delete;
791     } else {
792     last;
793     }
794    
795     $last = $prev;
796     }
797    
798     # prune some but not all insignificant whitespace
799     for my $ws (@{ $ppi->find (PPI::Token::Whitespace::) }) {
800     my $prev = $ws->previous_token;
801     my $next = $ws->next_token;
802    
803     if (!$prev || !$next) {
804     $ws->delete;
805     } else {
806     if (
807     $next->isa (PPI::Token::Operator::) && $next->{content} =~ /^(?:,|=|!|!=|==|=>)$/ # no ., because of digits. == float
808     or $prev->isa (PPI::Token::Operator::) && $prev->{content} =~ /^(?:,|=|\.|!|!=|==|=>)$/
809     or $prev->isa (PPI::Token::Structure::)
810     or ($OPTIMISE_SIZE &&
811     ($prev->isa (PPI::Token::Word::)
812     && (PPI::Token::Symbol:: eq ref $next
813     || $next->isa (PPI::Structure::Block::)
814     || $next->isa (PPI::Structure::List::)
815     || $next->isa (PPI::Structure::Condition::)))
816     )
817     ) {
818     $ws->delete;
819     } elsif ($prev->isa (PPI::Token::Whitespace::)) {
820     $ws->{content} = ' ';
821     $prev->delete;
822     } else {
823     $ws->{content} = ' ';
824     }
825     }
826     }
827    
828     # prune whitespace around blocks
829     if ($OPTIMISE_SIZE) {
830     # these usually decrease size, but decrease compressability more
831     for my $struct (PPI::Structure::Block::, PPI::Structure::Condition::) {
832     for my $node (@{ $ppi->find ($struct) }) {
833     my $n1 = $node->first_token;
834     my $n2 = $n1->previous_token;
835     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
836     $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
837     my $n1 = $node->last_token;
838     my $n2 = $n1->next_token;
839     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
840     $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
841     }
842     }
843    
844     for my $node (@{ $ppi->find (PPI::Structure::List::) }) {
845     my $n1 = $node->first_token;
846     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
847     my $n1 = $node->last_token;
848     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
849     }
850     }
851    
852     # reformat qw() lists which often have lots of whitespace
853     for my $node (@{ $ppi->find (PPI::Token::QuoteLike::Words::) }) {
854     if ($node->{content} =~ /^qw(.)(.*)(.)$/s) {
855     my ($a, $qw, $b) = ($1, $2, $3);
856     $qw =~ s/^\s+//;
857     $qw =~ s/\s+$//;
858     $qw =~ s/\s+/ /g;
859     $node->{content} = "qw$a$qw$b";
860     }
861     }
862    
863     $src = $ppi->serialize;
864     } else {
865     warn "WARNING: $pm{$pm}: PPI failed to parse this file\n";
866     }
867     } elsif ($STRIP =~ /pod/i && $pm ne "Opcode.pm") { # opcode parses its own pod
868     require Pod::Strip;
869    
870     my $stripper = Pod::Strip->new;
871    
872     my $out;
873     $stripper->output_string (\$out);
874     $stripper->parse_string_document ($src)
875     or die;
876     $src = $out;
877     }
878    
879     if ($VERIFY && $pm =~ /\.pm$/ && $pm ne "Opcode.pm") {
880     if (open my $fh, "-|") {
881     <$fh>;
882     } else {
883     eval "#line 1 \"$pm\"\n$src" or warn "\n\n\n$pm\n\n$src\n$@\n\n\n";
884     exit 0;
885     }
886     }
887    
888     $src
889     };
890    
891     # if ($pm eq "Opcode.pm") {
892     # open my $fh, ">x" or die; print $fh $src;#d#
893     # exit 1;
894     # }
895     }
896    
897     print "adding $pm (original size $size, stored size ", length $src, ")\n"
898     if $VERBOSE >= 2;
899    
900     push @index, ((length $pm) << 25) | length $data;
901     $data .= $pm . $src;
902     }
903    
904     length $data < 2**25
905     or die "ERROR: bundle too large (only 32MB supported)\n";
906    
907     my $varpfx = "bundle_" . substr +(Digest::MD5::md5_hex $data), 0, 16;
908    
909     #############################################################################
910     # output
911    
912     print "generating $PREFIX.h... "
913     if $VERBOSE >= 1;
914    
915     {
916     open my $fh, ">", "$PREFIX.h"
917     or die "$PREFIX.h: $!\n";
918    
919     print $fh <<EOF;
920     /* do not edit, automatically created by mkstaticbundle */
921    
922     #include <EXTERN.h>
923     #include <perl.h>
924     #include <XSUB.h>
925    
926     /* public API */
927     EXTERN_C PerlInterpreter *staticperl;
928     EXTERN_C void staticperl_xs_init (pTHX);
929     EXTERN_C void staticperl_init (void);
930     EXTERN_C void staticperl_cleanup (void);
931    
932     EOF
933     }
934    
935     print "\n"
936     if $VERBOSE >= 1;
937    
938     #############################################################################
939     # output
940    
941     print "generating $PREFIX.c... "
942     if $VERBOSE >= 1;
943    
944     open my $fh, ">", "$PREFIX.c"
945     or die "$PREFIX.c: $!\n";
946    
947     print $fh <<EOF;
948     /* do not edit, automatically created by mkstaticbundle */
949    
950     #include "bundle.h"
951    
952     /* public API */
953     PerlInterpreter *staticperl;
954    
955     EOF
956    
957     #############################################################################
958     # bundle data
959    
960     my $count = @index;
961    
962     print $fh <<EOF;
963     #include "bundle.h"
964    
965     /* bundle data */
966    
967     static const U32 $varpfx\_count = $count;
968     static const U32 $varpfx\_index [$count + 1] = {
969     EOF
970    
971     my $col;
972     for (@index) {
973     printf $fh "0x%08x,", $_;
974     print $fh "\n" unless ++$col % 10;
975    
976     }
977     printf $fh "0x%08x\n};\n", (length $data);
978    
979     print $fh "static const char $varpfx\_data [] =\n";
980     dump_string $fh, $data;
981    
982     print $fh ";\n\n";
983    
984     #############################################################################
985     # bootstrap
986    
987     # boot file for staticperl
988     # this file will be eval'ed at initialisation time
989    
990     my $bootstrap = '
991     BEGIN {
992     package ' . $PACKAGE . ';
993    
994     PerlIO::scalar->bootstrap;
995    
996     @INC = sub {
997     my $data = find "$_[1]"
998     or return;
999    
1000     $INC{$_[1]} = $_[1];
1001    
1002     open my $fh, "<", \$data;
1003     $fh
1004     };
1005     }
1006     ';
1007    
1008     $bootstrap .= "require '//boot';"
1009     if exists $pm{"//boot"};
1010    
1011     $bootstrap =~ s/\s+/ /g;
1012     $bootstrap =~ s/(\W) /$1/g;
1013     $bootstrap =~ s/ (\W)/$1/g;
1014    
1015     print $fh "const char bootstrap [] = ";
1016     dump_string $fh, $bootstrap;
1017     print $fh ";\n\n";
1018    
1019     print $fh <<EOF;
1020     /* search all bundles for the given file, using binary search */
1021     XS(find)
1022     {
1023     dXSARGS;
1024    
1025     if (items != 1)
1026     Perl_croak (aTHX_ "Usage: $PACKAGE\::find (\$path)");
1027    
1028     {
1029     STRLEN namelen;
1030     char *name = SvPV (ST (0), namelen);
1031     SV *res = 0;
1032    
1033     int l = 0, r = $varpfx\_count;
1034    
1035     while (l <= r)
1036     {
1037     int m = (l + r) >> 1;
1038     U32 idx = $varpfx\_index [m];
1039     int comp = namelen - (idx >> 25);
1040    
1041     if (!comp)
1042     {
1043     int ofs = idx & 0x1FFFFFFU;
1044     comp = memcmp (name, $varpfx\_data + ofs, namelen);
1045    
1046     if (!comp)
1047     {
1048     /* found */
1049     int ofs2 = $varpfx\_index [m + 1] & 0x1FFFFFFU;
1050    
1051     ofs += namelen;
1052     res = newSVpvn ($varpfx\_data + ofs, ofs2 - ofs);
1053     goto found;
1054     }
1055     }
1056    
1057     if (comp < 0)
1058     r = m - 1;
1059     else
1060     l = m + 1;
1061     }
1062    
1063     XSRETURN (0);
1064    
1065     found:
1066     ST (0) = res;
1067     sv_2mortal (ST (0));
1068     }
1069    
1070     XSRETURN (1);
1071     }
1072    
1073     /* list all files in the bundle */
1074     XS(list)
1075     {
1076     dXSARGS;
1077    
1078     if (items != 0)
1079     Perl_croak (aTHX_ "Usage: $PACKAGE\::list");
1080    
1081     {
1082     int i;
1083    
1084     EXTEND (SP, $varpfx\_count);
1085    
1086     for (i = 0; i < $varpfx\_count; ++i)
1087     {
1088     U32 idx = $varpfx\_index [i];
1089    
1090     PUSHs (newSVpvn ($varpfx\_data + (idx & 0x1FFFFFFU), idx >> 25));
1091     }
1092     }
1093    
1094     XSRETURN ($varpfx\_count);
1095     }
1096    
1097     EOF
1098    
1099     #############################################################################
1100     # xs_init
1101    
1102     print $fh <<EOF;
1103     void
1104     staticperl_xs_init (pTHX)
1105     {
1106     EOF
1107    
1108     @static_ext = ("DynaLoader", sort @static_ext);
1109    
1110     # prototypes
1111     for (@static_ext) {
1112     s/\.pm$//;
1113     (my $cname = $_) =~ s/\//__/g;
1114     print $fh " EXTERN_C void boot_$cname (pTHX_ CV* cv);\n";
1115     }
1116    
1117     print $fh <<EOF;
1118     char *file = __FILE__;
1119     dXSUB_SYS;
1120    
1121     newXSproto ("$PACKAGE\::find", find, file, "\$");
1122     newXSproto ("$PACKAGE\::list", list, file, "");
1123     EOF
1124    
1125     # calls
1126     for (@static_ext) {
1127     s/\.pm$//;
1128    
1129     (my $cname = $_) =~ s/\//__/g;
1130     (my $pname = $_) =~ s/\//::/g;
1131    
1132     my $bootstrap = $pname eq "DynaLoader" ? "boot" : "bootstrap";
1133    
1134     print $fh " newXS (\"$pname\::$bootstrap\", boot_$cname, file);\n";
1135     }
1136    
1137     print $fh <<EOF;
1138     Perl_av_create_and_unshift_one (&PL_preambleav, newSVpv (bootstrap, sizeof (bootstrap) - 1));
1139     }
1140     EOF
1141    
1142     #############################################################################
1143     # optional perl_init/perl_destroy
1144    
1145     if ($APP) {
1146     print $fh <<EOF;
1147    
1148     int
1149     main (int argc, char *argv [])
1150     {
1151     extern char **environ;
1152     int exitstatus;
1153    
1154     static char *args[] = {
1155     "staticperl",
1156     "-e",
1157     "0"
1158     };
1159    
1160     PERL_SYS_INIT3 (&argc, &argv, &environ);
1161     staticperl = perl_alloc ();
1162     perl_construct (staticperl);
1163    
1164     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1165    
1166     exitstatus = perl_parse (staticperl, staticperl_xs_init, sizeof (args) / sizeof (*args), args, environ);
1167     if (!exitstatus)
1168     perl_run (staticperl);
1169    
1170     exitstatus = perl_destruct (staticperl);
1171     perl_free (staticperl);
1172     PERL_SYS_TERM ();
1173    
1174     return exitstatus;
1175     }
1176     EOF
1177     } elsif ($PERL) {
1178     print $fh <<EOF;
1179    
1180     int
1181     main (int argc, char *argv [])
1182     {
1183     extern char **environ;
1184     int exitstatus;
1185    
1186     PERL_SYS_INIT3 (&argc, &argv, &environ);
1187     staticperl = perl_alloc ();
1188     perl_construct (staticperl);
1189    
1190     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1191    
1192     exitstatus = perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
1193     if (!exitstatus)
1194     perl_run (staticperl);
1195    
1196     exitstatus = perl_destruct (staticperl);
1197     perl_free (staticperl);
1198     PERL_SYS_TERM ();
1199    
1200     return exitstatus;
1201     }
1202     EOF
1203     } else {
1204     print $fh <<EOF;
1205    
1206     EXTERN_C void
1207     staticperl_init (void)
1208     {
1209     extern char **environ;
1210     int argc = sizeof (args) / sizeof (args [0]);
1211     char **argv = args;
1212    
1213     static char *args[] = {
1214     "staticperl",
1215     "-e",
1216     "0"
1217     };
1218    
1219     PERL_SYS_INIT3 (&argc, &argv, &environ);
1220     staticperl = perl_alloc ();
1221     perl_construct (staticperl);
1222     PL_origalen = 1;
1223     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1224     perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
1225    
1226     perl_run (staticperl);
1227     }
1228    
1229     EXTERN_C void
1230     staticperl_cleanup (void)
1231     {
1232     perl_destruct (staticperl);
1233     perl_free (staticperl);
1234     staticperl = 0;
1235     PERL_SYS_TERM ();
1236     }
1237     EOF
1238     }
1239    
1240     print -s "$PREFIX.c", " octets (", (length $data) , " data octets).\n\n"
1241     if $VERBOSE >= 1;
1242    
1243     #############################################################################
1244     # libs, cflags
1245    
1246     {
1247     print "generating $PREFIX.ccopts... "
1248     if $VERBOSE >= 1;
1249    
1250     my $str = "$Config{ccflags} $Config{optimize} $Config{cppflags} -I$Config{archlibexp}/CORE";
1251     $str =~ s/([\(\)])/\\$1/g;
1252    
1253     open my $fh, ">$PREFIX.ccopts"
1254     or die "$PREFIX.ccopts: $!";
1255     print $fh $str;
1256    
1257     print "$str\n\n"
1258     if $VERBOSE >= 1;
1259     }
1260    
1261     {
1262     print "generating $PREFIX.ldopts... ";
1263    
1264     my $str = $STATIC ? "-static " : "";
1265    
1266     $str .= "$Config{ccdlflags} $Config{ldflags} @libs $Config{archlibexp}/CORE/$Config{libperl} $Config{perllibs}";
1267    
1268     my %seen;
1269     $str .= " $_" for grep !$seen{$_}++, ($extralibs =~ /(\S+)/g);
1270    
1271     for (@staticlibs) {
1272     $str =~ s/(^|\s) (-l\Q$_\E) ($|\s)/$1-Wl,-Bstatic $2 -Wl,-Bdynamic$3/gx;
1273     }
1274    
1275     $str =~ s/([\(\)])/\\$1/g;
1276    
1277     open my $fh, ">$PREFIX.ldopts"
1278     or die "$PREFIX.ldopts: $!";
1279     print $fh $str;
1280    
1281     print "$str\n\n"
1282     if $VERBOSE >= 1;
1283     }
1284    
1285     if ($PERL or defined $APP) {
1286     $APP = "perl" unless defined $APP;
1287    
1288     print "building $APP...\n"
1289     if $VERBOSE >= 1;
1290    
1291     system "$Config{cc} \$(cat bundle.ccopts\) -o \Q$APP\E bundle.c \$(cat bundle.ldopts\)";
1292    
1293     unlink "$PREFIX.$_"
1294     for qw(ccopts ldopts c h);
1295    
1296     print "\n"
1297     if $VERBOSE >= 1;
1298     }
1299