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