ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Perl-Strip/bin/perlstrip
Revision: 1.1
Committed: Sat Jan 8 05:40:06 2011 UTC (15 years, 5 months ago) by root
Branch: MAIN
CVS Tags: rel-1_0
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.1 #!/opt/bin/perl
2    
3     =head1 NAME
4    
5     perlstrip - make perl sources smaller by removing comments, pod, whitespace...
6    
7     =head1 SYNOPSIS
8    
9     perlstrip file...
10     perlstrip --size --cache file...
11    
12     -v verbose
13     --size optimise for size (also: -s)
14     --cache use a cache (also: -c)
15     --cache-dir path cache directory to use
16     --output path output file for next input file (also: -o)
17    
18     WARNING: like its counterpart strip, by default files are overwritten!
19    
20     =head1 DESCRIPTION
21    
22     This program can be used to reduce the filesize of perl sources, for
23     example, before bundling them using L<PAR>, L<App::Staticperl>, or when
24     using them in a situation where disk space is premium (such as within
25     firmwares, boot floppies etc.).
26    
27     It does this by removing unnecessary whitespace, commands, POD and a few
28     other things.
29    
30     By default, it works through the list of files given on the commandline,
31     strips them and writes them again. This can be influenced via switches.
32    
33     =head2 OPTIONS
34    
35     =over 4
36    
37     =item C<--verbose>
38    
39     =item C<-v>
40    
41     Increases verbosity - highly recommended for interactive use.
42    
43     =item C<--output> path
44    
45     =item C<-o> path
46    
47     Write the stripped contents of the I<following> files on the commandline
48     to this path, instead of overwriting the original file. This can be used
49     to copy files instead of (destructively) overwriting them, by specifying
50     C<-o> before each file, e.g.:
51    
52     perlstrip -o strippedfile origsource
53    
54     =item C<--size>
55    
56     =item C<-s>
57    
58     Optimise for size instead of for compressibility - the default is to
59     optimise the file for later compression. Optimising for size makes the raw
60     file size smaller, but might compress a bit worse.
61    
62     =item C<--cache>
63    
64     =item C<-c>
65    
66     Stripping files can take a very long time. When this option is enabled,
67     then C<perlstrip> will cache larger files in a special directory
68     (default: F<~/.cache/perlstrip>), to improve repeated calls to strip
69     sources.
70    
71     This is mainly useful when you strip a whole perl installation repeatedly.
72    
73     =item C<--cache-dir> path
74    
75     Enables caching, but uses an alternative cache directory (which will be
76     created if it doesn't exist yet).
77    
78     =back
79    
80     =item SEE ALSO
81    
82     L<App::Staticperl>, L<Perl::Squish>.
83    
84     =head1 AUTHOR
85    
86     Marc Lehmann <schmorp@schmorp.de>
87     http://software.schmorp.de/pkg/staticperl.html
88    
89     =cut
90    
91     use Perl::Strip;
92     use Getopt::Long;
93     use common::sense;
94    
95     our $VERBOSE;
96     our $OPTIMISE_SIZE;
97     our $CACHE;
98     our $CACHEDIR;
99     our $OUTPUT;
100    
101     $|=1;
102    
103     sub usage {
104     require Pod::Usage;
105    
106     Pod::Usage::pod2usage (-output => *STDOUT, -verbose => 1, -exitval => 1, -noperldoc => 1);
107     }
108    
109     sub process {
110     my $file = shift;
111    
112     # GetOotions apparently wraps us into an eval :/
113     eval {
114     my @cache;
115    
116     if (defined $CACHEDIR) {
117     @cache = (cache => $CACHEDIR);
118     } elsif ($CACHE) {
119     mkdir "$ENV{HOME}/.cache";
120     @cache = (cache => "$ENV{HOME}/.cache/perlstrip");
121     }
122    
123     print "$file... " if $VERBOSE;
124    
125     my $output = defined $OUTPUT ? $OUTPUT : $file;
126    
127     my $src = do {
128     open my $fh, "<:perlio", $file
129     or die "$file: $!\n";
130    
131     local $/;
132     <$fh>
133     };
134    
135     printf "%d ", length $src if $VERBOSE;
136    
137     $src = (new Perl::Strip @cache, optimise_size => $OPTIMISE_SIZE)->strip ($src);
138    
139     printf "to %d bytes... ", length $src if $VERBOSE;
140     print $output eq $file ? "writing... " : "saving as $output... " if $VERBOSE;
141    
142     open my $fh, ">:perlio", "$output~"
143     or die "$output~: $!\n";
144     length $src == syswrite $fh, $src
145     or die "$output~: $!\n";
146     close $fh;
147     rename "$output~", $output;
148    
149     print "ok\n" if $VERBOSE;
150     };
151    
152     if ($@) {
153     print STDERR "$@\n";
154     exit 2;
155     }
156     }
157    
158     @ARGV
159     or usage;
160    
161     Getopt::Long::Configure ("bundling", "no_auto_abbrev", "no_ignore_case");
162    
163     GetOptions
164     "cache|c" => \$CACHE,
165     "cache-dir=s" => \$CACHEDIR,
166     "verbose|v" => sub { ++$VERBOSE },
167     "quiet|q" => sub { --$VERBOSE },
168     "size|s" => \$OPTIMISE_SIZE,
169     "output|o=s" => \$OUTPUT,
170     "<>" => \&process,
171     or usage;
172    
173     __END__
174    
175    
176     die "cannot specify both --app and --perl\n"
177     if $PERL and defined $APP;
178    
179     # required for @INC loading, unfortunately
180     trace_module "PerlIO::scalar";
181    
182     #############################################################################
183     # apply include/exclude
184    
185     {
186     my %pmi;
187    
188     for (@incext) {
189     my ($inc, $glob) = @$_;
190    
191     my @match = grep /$glob/, keys %pm;
192    
193     if ($inc) {
194     # include
195     @pmi{@match} = delete @pm{@match};
196    
197     print "applying include $glob - protected ", (scalar @match), " files.\n"
198     if $VERBOSE >= 5;
199     } else {
200     # exclude
201     delete @pm{@match};
202    
203     print "applying exclude $glob - removed ", (scalar @match), " files.\n"
204     if $VERBOSE >= 5;
205     }
206     }
207    
208     my @pmi = keys %pmi;
209     @pm{@pmi} = delete @pmi{@pmi};
210     }
211    
212     #############################################################################
213     # scan for AutoLoader, static archives and other dependencies
214    
215     sub scan_al {
216     my ($auto, $autodir) = @_;
217    
218     my $ix = "$autodir/autosplit.ix";
219    
220     print "processing autoload index for '$auto'\n"
221     if $VERBOSE >= 6;
222    
223     $pm{"$auto/autosplit.ix"} = $ix;
224    
225     open my $fh, "<:perlio", $ix
226     or die "$ix: $!";
227    
228     my $package;
229    
230     while (<$fh>) {
231     if (/^\s*sub\s+ ([^[:space:];]+) \s* (?:\([^)]*\))? \s*;?\s*$/x) {
232     my $al = "auto/$package/$1.al";
233     my $inc = find_inc $al;
234    
235     defined $inc or die "$al: autoload file not found, but should be there.\n";
236    
237     $pm{$al} = $inc;
238     print "found autoload function '$al'\n"
239     if $VERBOSE >= 6;
240    
241     } elsif (/^\s*package\s+([^[:space:];]+)\s*;?\s*$/) {
242     ($package = $1) =~ s/::/\//g;
243     } elsif (/^\s*(?:#|1?\s*;?\s*$)/) {
244     # nop
245     } else {
246     warn "WARNING: $ix: unparsable line, please report: $_";
247     }
248     }
249     }
250    
251     for my $pm (keys %pm) {
252     if ($pm =~ /^(.*)\.pm$/) {
253     my $auto = "auto/$1";
254     my $autodir = find_inc $auto;
255    
256     if (defined $autodir && -d $autodir) {
257     # AutoLoader
258     scan_al $auto, $autodir
259     if -f "$autodir/autosplit.ix";
260    
261     # extralibs.ld
262     if (open my $fh, "<:perlio", "$autodir/extralibs.ld") {
263     print "found extralibs for $pm\n"
264     if $VERBOSE >= 6;
265    
266     local $/;
267     $extralibs .= " " . <$fh>;
268     }
269    
270     $pm =~ /([^\/]+).pm$/ or die "$pm: unable to match last component";
271    
272     my $base = $1;
273    
274     # static ext
275     if (-f "$autodir/$base$Config{_a}") {
276     print "found static archive for $pm\n"
277     if $VERBOSE >= 3;
278    
279     push @libs, "$autodir/$base$Config{_a}";
280     push @static_ext, $pm;
281     }
282    
283     # dynamic object
284     die "ERROR: found shared object - can't link statically ($_)\n"
285     if -f "$autodir/$base.$Config{dlext}";
286    
287     if ($PACKLIST && open my $fh, "<:perlio", "$autodir/.packlist") {
288     print "found .packlist for $pm\n"
289     if $VERBOSE >= 3;
290    
291     while (<$fh>) {
292     chomp;
293     s/ .*$//; # newer-style .packlists might contain key=value pairs
294    
295     # only include certain files (.al, .ix, .pm, .pl)
296     if (/\.(pm|pl|al|ix)$/) {
297     for my $inc (@INC) {
298     # in addition, we only add files that are below some @INC path
299     $inc =~ s/\/*$/\//;
300    
301     if ($inc eq substr $_, 0, length $inc) {
302     my $base = substr $_, length $inc;
303     $pm{$base} = $_;
304    
305     print "+ added .packlist dependency $base\n"
306     if $VERBOSE >= 3;
307     }
308    
309     last;
310     }
311     }
312     }
313     }
314     }
315     }
316     }
317    
318     #############################################################################
319    
320     print "processing bundle files (try more -v power if you get bored waiting here)...\n"
321     if $VERBOSE >= 1;
322    
323     my $data;
324     my @index;
325     my @order = sort {
326     length $a <=> length $b
327     or $a cmp $b
328     } keys %pm;
329    
330     # sorting by name - better compression, but needs more metadata
331     # sorting by length - faster lookup
332     # usually, the metadata overhead beats the loss through compression
333    
334     for my $pm (@order) {
335     my $path = $pm{$pm};
336    
337     128 > length $pm
338     or die "ERROR: $pm: path too long (only 128 octets supported)\n";
339    
340     my $src = ref $path
341     ? $$path
342     : do {
343     open my $pm, "<", $path
344     or die "$path: $!";
345    
346     local $/;
347    
348     <$pm>
349     };
350    
351     my $size = length $src;
352    
353     unless ($pmbin{$pm}) { # only do this unless the file is binary
354     if ($pm =~ /^auto\/POSIX\/[^\/]+\.al$/) {
355     if ($src =~ /^ unimpl \"/m) {
356     print "$pm: skipping (raises runtime error only).\n"
357     if $VERBOSE >= 3;
358     next;
359     }
360     }
361    
362     $src = cache +($STRIP eq "ppi" ? "$UNISTRIP,$OPTIMISE_SIZE" : undef), $src, sub {
363     if ($UNISTRIP && $pm =~ /^unicore\/.*\.pl$/) {
364     print "applying unicore stripping $pm\n"
365     if $VERBOSE >= 6;
366    
367     # special stripping for unicore swashes and properties
368     # much more could be done by going binary
369     $src =~ s{
370     (^return\ <<'END';\n) (.*?\n) (END(?:\n|\Z))
371     }{
372     my ($pre, $data, $post) = ($1, $2, $3);
373    
374     for ($data) {
375     s/^([0-9a-fA-F]+)\t([0-9a-fA-F]+)\t/sprintf "%X\t%X", hex $1, hex $2/gem
376     if $OPTIMISE_SIZE;
377    
378     # s{
379     # ^([0-9a-fA-F]+)\t([0-9a-fA-F]*)\t
380     # }{
381     # # ww - smaller filesize, UU - compress better
382     # pack "C0UU",
383     # hex $1,
384     # length $2 ? (hex $2) - (hex $1) : 0
385     # }gemx;
386    
387     s/#.*\n/\n/mg;
388     s/\s+\n/\n/mg;
389     }
390    
391     "$pre$data$post"
392     }smex;
393     }
394    
395     if ($STRIP =~ /ppi/i) {
396     require PPI;
397    
398     if (my $ppi = PPI::Document->new (\$src)) {
399     $ppi->prune ("PPI::Token::Comment");
400     $ppi->prune ("PPI::Token::Pod");
401    
402     # prune END stuff
403     for (my $last = $ppi->last_element; $last; ) {
404     my $prev = $last->previous_token;
405    
406     if ($last->isa (PPI::Token::Whitespace::)) {
407     $last->delete;
408     } elsif ($last->isa (PPI::Statement::End::)) {
409     $last->delete;
410     last;
411     } elsif ($last->isa (PPI::Token::Pod::)) {
412     $last->delete;
413     } else {
414     last;
415     }
416    
417     $last = $prev;
418     }
419    
420     # prune some but not all insignificant whitespace
421     for my $ws (@{ $ppi->find (PPI::Token::Whitespace::) }) {
422     my $prev = $ws->previous_token;
423     my $next = $ws->next_token;
424    
425     if (!$prev || !$next) {
426     $ws->delete;
427     } else {
428     if (
429     $next->isa (PPI::Token::Operator::) && $next->{content} =~ /^(?:,|=|!|!=|==|=>)$/ # no ., because of digits. == float
430     or $prev->isa (PPI::Token::Operator::) && $prev->{content} =~ /^(?:,|=|\.|!|!=|==|=>)$/
431     or $prev->isa (PPI::Token::Structure::)
432     or ($OPTIMISE_SIZE &&
433     ($prev->isa (PPI::Token::Word::)
434     && (PPI::Token::Symbol:: eq ref $next
435     || $next->isa (PPI::Structure::Block::)
436     || $next->isa (PPI::Structure::List::)
437     || $next->isa (PPI::Structure::Condition::)))
438     )
439     ) {
440     $ws->delete;
441     } elsif ($prev->isa (PPI::Token::Whitespace::)) {
442     $ws->{content} = ' ';
443     $prev->delete;
444     } else {
445     $ws->{content} = ' ';
446     }
447     }
448     }
449    
450     # prune whitespace around blocks
451     if ($OPTIMISE_SIZE) {
452     # these usually decrease size, but decrease compressability more
453     for my $struct (PPI::Structure::Block::, PPI::Structure::Condition::) {
454     for my $node (@{ $ppi->find ($struct) }) {
455     my $n1 = $node->first_token;
456     my $n2 = $n1->previous_token;
457     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
458     $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
459     my $n1 = $node->last_token;
460     my $n2 = $n1->next_token;
461     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
462     $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
463     }
464     }
465    
466     for my $node (@{ $ppi->find (PPI::Structure::List::) }) {
467     my $n1 = $node->first_token;
468     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
469     my $n1 = $node->last_token;
470     $n1->delete if $n1->isa (PPI::Token::Whitespace::);
471     }
472     }
473    
474     # reformat qw() lists which often have lots of whitespace
475     for my $node (@{ $ppi->find (PPI::Token::QuoteLike::Words::) }) {
476     if ($node->{content} =~ /^qw(.)(.*)(.)$/s) {
477     my ($a, $qw, $b) = ($1, $2, $3);
478     $qw =~ s/^\s+//;
479     $qw =~ s/\s+$//;
480     $qw =~ s/\s+/ /g;
481     $node->{content} = "qw$a$qw$b";
482     }
483     }
484    
485     $src = $ppi->serialize;
486     } else {
487     warn "WARNING: $pm{$pm}: PPI failed to parse this file\n";
488     }
489     } elsif ($STRIP =~ /pod/i && $pm ne "Opcode.pm") { # opcode parses its own pod
490     require Pod::Strip;
491    
492     my $stripper = Pod::Strip->new;
493    
494     my $out;
495     $stripper->output_string (\$out);
496     $stripper->parse_string_document ($src)
497     or die;
498     $src = $out;
499     }
500    
501     if ($VERIFY && $pm =~ /\.pm$/ && $pm ne "Opcode.pm") {
502     if (open my $fh, "-|") {
503     <$fh>;
504     } else {
505     eval "#line 1 \"$pm\"\n$src" or warn "\n\n\n$pm\n\n$src\n$@\n\n\n";
506     exit 0;
507     }
508     }
509    
510     $src
511     };
512    
513     # if ($pm eq "Opcode.pm") {
514     # open my $fh, ">x" or die; print $fh $src;#d#
515     # exit 1;
516     # }
517     }
518    
519     print "adding $pm (original size $size, stored size ", length $src, ")\n"
520     if $VERBOSE >= 2;
521    
522     push @index, ((length $pm) << 25) | length $data;
523     $data .= $pm . $src;
524     }
525    
526     length $data < 2**25
527     or die "ERROR: bundle too large (only 32MB supported)\n";
528    
529     my $varpfx = "bundle_" . substr +(Digest::MD5::md5_hex $data), 0, 16;
530    
531     #############################################################################
532     # output
533    
534     print "generating $PREFIX.h... "
535     if $VERBOSE >= 1;
536    
537     {
538     open my $fh, ">", "$PREFIX.h"
539     or die "$PREFIX.h: $!\n";
540    
541     print $fh <<EOF;
542     /* do not edit, automatically created by mkstaticbundle */
543    
544     #include <EXTERN.h>
545     #include <perl.h>
546     #include <XSUB.h>
547    
548     /* public API */
549     EXTERN_C PerlInterpreter *staticperl;
550     EXTERN_C void staticperl_xs_init (pTHX);
551     EXTERN_C void staticperl_init (void);
552     EXTERN_C void staticperl_cleanup (void);
553    
554     EOF
555     }
556    
557     print "\n"
558     if $VERBOSE >= 1;
559    
560     #############################################################################
561     # output
562    
563     print "generating $PREFIX.c... "
564     if $VERBOSE >= 1;
565    
566     open my $fh, ">", "$PREFIX.c"
567     or die "$PREFIX.c: $!\n";
568    
569     print $fh <<EOF;
570     /* do not edit, automatically created by mkstaticbundle */
571    
572     #include "bundle.h"
573    
574     /* public API */
575     PerlInterpreter *staticperl;
576    
577     EOF
578    
579     #############################################################################
580     # bundle data
581    
582     my $count = @index;
583    
584     print $fh <<EOF;
585     #include "bundle.h"
586    
587     /* bundle data */
588    
589     static const U32 $varpfx\_count = $count;
590     static const U32 $varpfx\_index [$count + 1] = {
591     EOF
592    
593     my $col;
594     for (@index) {
595     printf $fh "0x%08x,", $_;
596     print $fh "\n" unless ++$col % 10;
597    
598     }
599     printf $fh "0x%08x\n};\n", (length $data);
600    
601     print $fh "static const char $varpfx\_data [] =\n";
602     dump_string $fh, $data;
603    
604     print $fh ";\n\n";
605    
606     #############################################################################
607     # bootstrap
608    
609     # boot file for staticperl
610     # this file will be eval'ed at initialisation time
611    
612     my $bootstrap = '
613     BEGIN {
614     package ' . $PACKAGE . ';
615    
616     PerlIO::scalar->bootstrap;
617    
618     @INC = sub {
619     my $data = find "$_[1]"
620     or return;
621    
622     $INC{$_[1]} = $_[1];
623    
624     open my $fh, "<", \$data;
625     $fh
626     };
627     }
628     ';
629    
630     $bootstrap .= "require '//boot';"
631     if exists $pm{"//boot"};
632    
633     $bootstrap =~ s/\s+/ /g;
634     $bootstrap =~ s/(\W) /$1/g;
635     $bootstrap =~ s/ (\W)/$1/g;
636    
637     print $fh "const char bootstrap [] = ";
638     dump_string $fh, $bootstrap;
639     print $fh ";\n\n";
640    
641     print $fh <<EOF;
642     /* search all bundles for the given file, using binary search */
643     XS(find)
644     {
645     dXSARGS;
646    
647     if (items != 1)
648     Perl_croak (aTHX_ "Usage: $PACKAGE\::find (\$path)");
649    
650     {
651     STRLEN namelen;
652     char *name = SvPV (ST (0), namelen);
653     SV *res = 0;
654    
655     int l = 0, r = $varpfx\_count;
656    
657     while (l <= r)
658     {
659     int m = (l + r) >> 1;
660     U32 idx = $varpfx\_index [m];
661     int comp = namelen - (idx >> 25);
662    
663     if (!comp)
664     {
665     int ofs = idx & 0x1FFFFFFU;
666     comp = memcmp (name, $varpfx\_data + ofs, namelen);
667    
668     if (!comp)
669     {
670     /* found */
671     int ofs2 = $varpfx\_index [m + 1] & 0x1FFFFFFU;
672    
673     ofs += namelen;
674     res = newSVpvn ($varpfx\_data + ofs, ofs2 - ofs);
675     goto found;
676     }
677     }
678    
679     if (comp < 0)
680     r = m - 1;
681     else
682     l = m + 1;
683     }
684    
685     XSRETURN (0);
686    
687     found:
688     ST (0) = res;
689     sv_2mortal (ST (0));
690     }
691    
692     XSRETURN (1);
693     }
694    
695     /* list all files in the bundle */
696     XS(list)
697     {
698     dXSARGS;
699    
700     if (items != 0)
701     Perl_croak (aTHX_ "Usage: $PACKAGE\::list");
702    
703     {
704     int i;
705    
706     EXTEND (SP, $varpfx\_count);
707    
708     for (i = 0; i < $varpfx\_count; ++i)
709     {
710     U32 idx = $varpfx\_index [i];
711    
712     PUSHs (newSVpvn ($varpfx\_data + (idx & 0x1FFFFFFU), idx >> 25));
713     }
714     }
715    
716     XSRETURN ($varpfx\_count);
717     }
718    
719     EOF
720    
721     #############################################################################
722     # xs_init
723    
724     print $fh <<EOF;
725     void
726     staticperl_xs_init (pTHX)
727     {
728     EOF
729    
730     @static_ext = ("DynaLoader", sort @static_ext);
731    
732     # prototypes
733     for (@static_ext) {
734     s/\.pm$//;
735     (my $cname = $_) =~ s/\//__/g;
736     print $fh " EXTERN_C void boot_$cname (pTHX_ CV* cv);\n";
737     }
738    
739     print $fh <<EOF;
740     char *file = __FILE__;
741     dXSUB_SYS;
742    
743     newXSproto ("$PACKAGE\::find", find, file, "\$");
744     newXSproto ("$PACKAGE\::list", list, file, "");
745     EOF
746    
747     # calls
748     for (@static_ext) {
749     s/\.pm$//;
750    
751     (my $cname = $_) =~ s/\//__/g;
752     (my $pname = $_) =~ s/\//::/g;
753    
754     my $bootstrap = $pname eq "DynaLoader" ? "boot" : "bootstrap";
755    
756     print $fh " newXS (\"$pname\::$bootstrap\", boot_$cname, file);\n";
757     }
758    
759     print $fh <<EOF;
760     Perl_av_create_and_unshift_one (&PL_preambleav, newSVpv (bootstrap, sizeof (bootstrap) - 1));
761     }
762     EOF
763    
764     #############################################################################
765     # optional perl_init/perl_destroy
766    
767     if ($APP) {
768     print $fh <<EOF;
769    
770     int
771     main (int argc, char *argv [])
772     {
773     extern char **environ;
774     int exitstatus;
775    
776     static char *args[] = {
777     "staticperl",
778     "-e",
779     "0"
780     };
781    
782     PERL_SYS_INIT3 (&argc, &argv, &environ);
783     staticperl = perl_alloc ();
784     perl_construct (staticperl);
785    
786     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
787    
788     exitstatus = perl_parse (staticperl, staticperl_xs_init, sizeof (args) / sizeof (*args), args, environ);
789     if (!exitstatus)
790     perl_run (staticperl);
791    
792     exitstatus = perl_destruct (staticperl);
793     perl_free (staticperl);
794     PERL_SYS_TERM ();
795    
796     return exitstatus;
797     }
798     EOF
799     } elsif ($PERL) {
800     print $fh <<EOF;
801    
802     int
803     main (int argc, char *argv [])
804     {
805     extern char **environ;
806     int exitstatus;
807    
808     PERL_SYS_INIT3 (&argc, &argv, &environ);
809     staticperl = perl_alloc ();
810     perl_construct (staticperl);
811    
812     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
813    
814     exitstatus = perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
815     if (!exitstatus)
816     perl_run (staticperl);
817    
818     exitstatus = perl_destruct (staticperl);
819     perl_free (staticperl);
820     PERL_SYS_TERM ();
821    
822     return exitstatus;
823     }
824     EOF
825     } else {
826     print $fh <<EOF;
827    
828     EXTERN_C void
829     staticperl_init (void)
830     {
831     extern char **environ;
832     int argc = sizeof (args) / sizeof (args [0]);
833     char **argv = args;
834    
835     static char *args[] = {
836     "staticperl",
837     "-e",
838     "0"
839     };
840    
841     PERL_SYS_INIT3 (&argc, &argv, &environ);
842     staticperl = perl_alloc ();
843     perl_construct (staticperl);
844     PL_origalen = 1;
845     PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
846     perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
847    
848     perl_run (staticperl);
849     }
850    
851     EXTERN_C void
852     staticperl_cleanup (void)
853     {
854     perl_destruct (staticperl);
855     perl_free (staticperl);
856     staticperl = 0;
857     PERL_SYS_TERM ();
858     }
859     EOF
860     }
861    
862     print -s "$PREFIX.c", " octets (", (length $data) , " data octets).\n\n"
863     if $VERBOSE >= 1;
864    
865     #############################################################################
866     # libs, cflags
867    
868     {
869     print "generating $PREFIX.ccopts... "
870     if $VERBOSE >= 1;
871    
872     my $str = "$Config{ccflags} $Config{optimize} $Config{cppflags} -I$Config{archlibexp}/CORE";
873     $str =~ s/([\(\)])/\\$1/g;
874    
875     open my $fh, ">$PREFIX.ccopts"
876     or die "$PREFIX.ccopts: $!";
877     print $fh $str;
878    
879     print "$str\n\n"
880     if $VERBOSE >= 1;
881     }
882    
883     {
884     print "generating $PREFIX.ldopts... ";
885    
886     my $str = $STATIC ? "-static " : "";
887    
888     $str .= "$Config{ccdlflags} $Config{ldflags} @libs $Config{archlibexp}/CORE/$Config{libperl} $Config{perllibs}";
889    
890     my %seen;
891     $str .= " $_" for grep !$seen{$_}++, ($extralibs =~ /(\S+)/g);
892    
893     for (@staticlibs) {
894     $str =~ s/(^|\s) (-l\Q$_\E) ($|\s)/$1-Wl,-Bstatic $2 -Wl,-Bdynamic$3/gx;
895     }
896    
897     $str =~ s/([\(\)])/\\$1/g;
898    
899     open my $fh, ">$PREFIX.ldopts"
900     or die "$PREFIX.ldopts: $!";
901     print $fh $str;
902    
903     print "$str\n\n"
904     if $VERBOSE >= 1;
905     }
906    
907     if ($PERL or defined $APP) {
908     $APP = "perl" unless defined $APP;
909    
910     print "building $APP...\n"
911     if $VERBOSE >= 1;
912    
913     system "$Config{cc} \$(cat bundle.ccopts\) -o \Q$APP\E bundle.c \$(cat bundle.ldopts\)";
914    
915     unlink "$PREFIX.$_"
916     for qw(ccopts ldopts c h);
917    
918     print "\n"
919     if $VERBOSE >= 1;
920     }
921