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

File Contents

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