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