ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/App-Staticperl/mkbundle
Revision: 1.28
Committed: Sat Jul 9 18:26:27 2011 UTC (12 years, 11 months ago) by root
Branch: MAIN
Changes since 1.27: +63 -10 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 #!/opt/bin/perl
2
3 #############################################################################
4 # cannot load modules till after the tracer BEGIN block
5
6 our $VERBOSE = 1;
7 our $STRIP = "pod"; # none, pod or ppi
8 our $UNISTRIP = 1; # always on, try to strip unicore swash data
9 our $PERL = 0;
10 our $APP;
11 our $VERIFY = 0;
12 our $STATIC = 0;
13 our $PACKLIST = 0;
14 our $IGNORE_ENV = 0;
15 our $ALLOW_DLLS = 0;
16 our $HAVE_DLLS; # maybe useful?
17
18 our $OPTIMISE_SIZE = 0; # optimise for raw file size instead of for compression?
19
20 our $CACHE;
21 our $CACHEVER = 1; # do not change unless you know what you are doing
22
23 my $PREFIX = "bundle";
24 my $PACKAGE = "static";
25
26 my %pm;
27 my %pmbin;
28 my @libs;
29 my @static_ext;
30 my $extralibs;
31 my @staticlibs;
32 my @incext;
33
34 @ARGV
35 or die "$0: use 'staticperl help' (or read the sources of staticperl)\n";
36
37 # remove "." from @INC - staticperl.sh does it for us, but be on the safe side
38 BEGIN { @INC = grep !/^\.$/, @INC }
39
40 $|=1;
41
42 our ($TRACER_W, $TRACER_R);
43
44 sub find_incdir($) {
45 for (@INC) {
46 next if ref;
47 return $_ if -e "$_/$_[0]";
48 }
49
50 undef
51 }
52
53 sub find_inc($) {
54 my $dir = find_incdir $_[0];
55
56 return "$dir/$_[0]"
57 if defined $dir;
58
59 undef
60 }
61
62 BEGIN {
63 # create a loader process to detect @INC requests before we load any modules
64 my ($W_TRACER, $R_TRACER); # used by tracer
65
66 pipe $R_TRACER, $TRACER_W or die "pipe: $!";
67 pipe $TRACER_R, $W_TRACER or die "pipe: $!";
68
69 unless (fork) {
70 close $TRACER_R;
71 close $TRACER_W;
72
73 my $pkg = "pkg000000";
74
75 unshift @INC, sub {
76 my $dir = find_incdir $_[1]
77 or return;
78
79 syswrite $W_TRACER, "-\n$dir\n$_[1]\n";
80
81 open my $fh, "<:perlio", "$dir/$_[1]"
82 or warn "ERROR: $dir/$_[1]: $!\n";
83
84 $fh
85 };
86
87 while (<$R_TRACER>) {
88 if (/use (.*)$/) {
89 my $mod = $1;
90 my $eval;
91
92 if ($mod =~ /^'.*'$/ or $mod =~ /^".*"$/) {
93 $eval = "require $mod";
94 } elsif ($mod =~ y%/.%%) {
95 $eval = "require q\x00$mod\x00";
96 } else {
97 my $pkg = ++$pkg;
98 $eval = "{ package $pkg; use $mod; }";
99 }
100
101 eval $eval;
102 warn "ERROR: $@ (while loading '$mod')\n"
103 if $@;
104 } elsif (/eval (.*)$/) {
105 my $eval = $1;
106 eval $eval;
107 warn "ERROR: $@ (in '$eval')\n"
108 if $@;
109 }
110
111 syswrite $W_TRACER, "\n";
112 }
113
114 exit 0;
115 }
116 }
117
118 # module loading is now safe
119
120 sub trace_parse {
121 for (;;) {
122 <$TRACER_R> =~ /^-$/ or last;
123 my $dir = <$TRACER_R>; chomp $dir;
124 my $name = <$TRACER_R>; chomp $name;
125
126 $pm{$name} = "$dir/$name";
127
128 print "+ found potential dependency $name\n"
129 if $VERBOSE >= 3;
130 }
131 }
132
133 sub trace_module {
134 print "tracing module $_[0]\n"
135 if $VERBOSE >= 2;
136
137 syswrite $TRACER_W, "use $_[0]\n";
138 trace_parse;
139 }
140
141 sub trace_eval {
142 print "tracing eval $_[0]\n"
143 if $VERBOSE >= 2;
144
145 syswrite $TRACER_W, "eval $_[0]\n";
146 trace_parse;
147 }
148
149 sub trace_finish {
150 close $TRACER_W;
151 close $TRACER_R;
152 }
153
154 #############################################################################
155 # now we can use modules
156
157 use common::sense;
158 use Config;
159 use Digest::MD5;
160
161 sub cache($$$) {
162 my ($variant, $src, $filter) = @_;
163
164 if (length $CACHE and 2048 <= length $src and defined $variant) {
165 my $file = "$CACHE/" . Digest::MD5::md5_hex "$CACHEVER\x00$variant\x00$src";
166
167 if (open my $fh, "<:perlio", $file) {
168 print "using cache for $file\n"
169 if $VERBOSE >= 7;
170
171 local $/;
172 return <$fh>;
173 }
174
175 $src = $filter->($src);
176
177 print "creating cache entry $file\n"
178 if $VERBOSE >= 8;
179
180 if (open my $fh, ">:perlio", "$file~") {
181 if ((syswrite $fh, $src) == length $src) {
182 close $fh;
183 rename "$file~", $file;
184 }
185 }
186
187 return $src;
188 }
189
190 $filter->($src)
191 }
192
193 sub dump_string {
194 my ($fh, $data) = @_;
195
196 if (length $data) {
197 for (
198 my $ofs = 0;
199 length (my $substr = substr $data, $ofs, 80);
200 $ofs += 80
201 ) {
202 $substr =~ s/([^\x20-\x21\x23-\x5b\x5d-\x7e])/sprintf "\\%03o", ord $1/ge;
203 $substr =~ s/\?/\\?/g; # trigraphs...
204 print $fh " \"$substr\"\n";
205 }
206 } else {
207 print $fh " \"\"\n";
208 }
209 }
210
211 #############################################################################
212
213 sub glob2re {
214 for (quotemeta $_[0]) {
215 s/\\\*/\x00/g;
216 s/\x00\x00/.*/g;
217 s/\x00/[^\/]*/g;
218 s/\\\?/[^\/]/g;
219
220 $_ = s/^\\\/// ? "^$_\$" : "(?:^|/)$_\$";
221
222 s/(?: \[\^\/\] | \. ) \*\$$//x;
223
224 return qr<$_>s
225 }
226 }
227
228 our %INCSKIP = (
229 "unicore/TestProp.pl" => undef, # 3.5MB of insanity, apparently just some testcase
230 );
231
232 sub get_dirtree {
233 my $root = shift;
234
235 my @tree;
236 my $skip;
237
238 my $scan; $scan = sub {
239 for (sort do {
240 opendir my $fh, $_[0]
241 or return;
242 readdir $fh
243 }) {
244 next if /^\./;
245
246 my $path = "$_[0]/$_";
247
248 if (-d "$path/.") {
249 $scan->($path);
250 } else {
251 $path = substr $path, $skip;
252 push @tree, $path
253 unless exists $INCSKIP{$path};
254 }
255 }
256 };
257
258 $root =~ s/\/$//;
259 $skip = 1 + length $root;
260 $scan->($root);
261
262 \@tree
263 }
264
265 my $inctrees;
266
267 sub get_inctrees {
268 unless ($inctrees) {
269 my %inctree;
270 $inctree{$_} ||= [$_, get_dirtree $_] # entries in @INC are often duplicates
271 for @INC;
272 $inctrees = [values %inctree];
273 }
274
275 @$inctrees
276 }
277
278 #############################################################################
279
280 sub cmd_boot {
281 $pm{"&&boot"} = $_[0];
282 }
283
284 sub cmd_add {
285 $_[0] =~ /^(.*?)(?:\s+(\S+))?$/
286 or die "$_[0]: cannot parse";
287
288 my $file = $1;
289 my $as = defined $2 ? $2 : $1;
290
291 $pm{$as} = $file;
292 $pmbin{$as} = 1 if $_[1];
293 }
294
295 sub cmd_staticlib {
296 push @staticlibs, $_
297 for split /\s+/, $_[0];
298 }
299
300 sub cmd_include {
301 push @incext, [$_[1], glob2re $_[0]];
302 }
303
304 sub cmd_incglob {
305 my ($pattern) = @_;
306
307 $pattern = glob2re $pattern;
308
309 for (get_inctrees) {
310 my ($dir, $files) = @$_;
311
312 $pm{$_} = "$dir/$_"
313 for grep /$pattern/ && /\.(pl|pm)$/, @$files;
314 }
315 }
316
317 sub parse_argv;
318
319 sub cmd_file {
320 open my $fh, "<", $_[0]
321 or die "$_[0]: $!\n";
322
323 local @ARGV;
324
325 while (<$fh>) {
326 chomp;
327 next unless /\S/;
328 next if /^\s*#/;
329
330 s/^\s*-*/--/;
331 my ($cmd, $args) = split / /, $_, 2;
332
333 push @ARGV, $cmd;
334 push @ARGV, $args if defined $args;
335 }
336
337 parse_argv;
338 }
339
340 use Getopt::Long;
341
342 sub parse_argv {
343 GetOptions
344 "perl" => \$PERL,
345 "app=s" => \$APP,
346
347 "verbose|v" => sub { ++$VERBOSE },
348 "quiet|q" => sub { --$VERBOSE },
349
350 "strip=s" => \$STRIP,
351 "cache=s" => \$CACHE, # internal option
352 "eval|e=s" => sub { trace_eval $_[1] },
353 "use|M=s" => sub { trace_module $_[1] },
354 "boot=s" => sub { cmd_boot $_[1] },
355 "add=s" => sub { cmd_add $_[1], 0 },
356 "addbin=s" => sub { cmd_add $_[1], 1 },
357 "incglob=s" => sub { cmd_incglob $_[1] },
358 "include|i=s" => sub { cmd_include $_[1], 1 },
359 "exclude|x=s" => sub { cmd_include $_[1], 0 },
360 "usepacklists!" => \$PACKLIST,
361
362 "static!" => \$STATIC,
363 "staticlib=s" => sub { cmd_staticlib $_[1] },
364 "allow-dlls" => \$ALLOW_DLLS,
365 "ignore-env" => \$IGNORE_ENV,
366
367 "<>" => sub { cmd_file $_[0] },
368 or exit 1;
369 }
370
371 Getopt::Long::Configure ("bundling", "no_auto_abbrev", "no_ignore_case");
372
373 parse_argv;
374
375 die "cannot specify both --app and --perl\n"
376 if $PERL and defined $APP;
377
378 # required for @INC loading, unfortunately
379 trace_module "PerlIO::scalar";
380
381 #############################################################################
382 # apply include/exclude
383
384 {
385 my %pmi;
386
387 for (@incext) {
388 my ($inc, $glob) = @$_;
389
390 my @match = grep /$glob/, keys %pm;
391
392 if ($inc) {
393 # include
394 @pmi{@match} = delete @pm{@match};
395
396 print "applying include $glob - protected ", (scalar @match), " files.\n"
397 if $VERBOSE >= 5;
398 } else {
399 # exclude
400 delete @pm{@match};
401
402 print "applying exclude $glob - removed ", (scalar @match), " files.\n"
403 if $VERBOSE >= 5;
404 }
405 }
406
407 my @pmi = keys %pmi;
408 @pm{@pmi} = delete @pmi{@pmi};
409 }
410
411 #############################################################################
412 # scan for AutoLoader, static archives and other dependencies
413
414 sub scan_al {
415 my ($auto, $autodir) = @_;
416
417 my $ix = "$autodir/autosplit.ix";
418
419 print "processing autoload index for '$auto'\n"
420 if $VERBOSE >= 6;
421
422 $pm{"$auto/autosplit.ix"} = $ix;
423
424 open my $fh, "<:perlio", $ix
425 or die "$ix: $!";
426
427 my $package;
428
429 while (<$fh>) {
430 if (/^\s*sub\s+ ([^[:space:];]+) \s* (?:\([^)]*\))? \s*;?\s*$/x) {
431 my $al = "auto/$package/$1.al";
432 my $inc = find_inc $al;
433
434 defined $inc or die "$al: autoload file not found, but should be there.\n";
435
436 $pm{$al} = $inc;
437 print "found autoload function '$al'\n"
438 if $VERBOSE >= 6;
439
440 } elsif (/^\s*package\s+([^[:space:];]+)\s*;?\s*$/) {
441 ($package = $1) =~ s/::/\//g;
442 } elsif (/^\s*(?:#|1?\s*;?\s*$)/) {
443 # nop
444 } else {
445 warn "WARNING: $ix: unparsable line, please report: $_";
446 }
447 }
448 }
449
450 for my $pm (keys %pm) {
451 if ($pm =~ /^(.*)\.pm$/) {
452 my $auto = "auto/$1";
453 my $autodir = find_inc $auto;
454
455 if (defined $autodir && -d $autodir) {
456 # AutoLoader
457 scan_al $auto, $autodir
458 if -f "$autodir/autosplit.ix";
459
460 # extralibs.ld
461 if (open my $fh, "<:perlio", "$autodir/extralibs.ld") {
462 print "found extralibs for $pm\n"
463 if $VERBOSE >= 6;
464
465 local $/;
466 $extralibs .= " " . <$fh>;
467 }
468
469 $pm =~ /([^\/]+).pm$/ or die "$pm: unable to match last component";
470
471 my $base = $1;
472
473 # static ext
474 if (-f "$autodir/$base$Config{_a}") {
475 print "found static archive for $pm\n"
476 if $VERBOSE >= 3;
477
478 push @libs, "$autodir/$base$Config{_a}";
479 push @static_ext, $pm;
480 }
481
482 # dynamic object
483 if (-f "$autodir/$base.$Config{dlext}") {
484 if ($ALLOW_DLLS) {
485 my $as = "&fs/perl/$auto/$base.$Config{dlext}";
486 $pm{$as} = "$autodir/$base.$Config{dlext}";
487 $pmbin{$as} = 1;
488
489 $HAVE_DLLS = 1;
490
491 print "+ added dynamic object $auto/$base.$Config{dlext}\n"
492 if $VERBOSE >= 3;
493 } else {
494 die "ERROR: found shared object '$_' but --allow-dlls not given, aborting.\n"
495 }
496 }
497
498 if ($PACKLIST && open my $fh, "<:perlio", "$autodir/.packlist") {
499 print "found .packlist for $pm\n"
500 if $VERBOSE >= 3;
501
502 while (<$fh>) {
503 chomp;
504 s/ .*$//; # newer-style .packlists might contain key=value pairs
505
506 # only include certain files (.al, .ix, .pm, .pl)
507 if (/\.(pm|pl|al|ix)$/) {
508 for my $inc (@INC) {
509 # in addition, we only add files that are below some @INC path
510 $inc =~ s/\/*$/\//;
511
512 if ($inc eq substr $_, 0, length $inc) {
513 my $base = substr $_, length $inc;
514 $pm{$base} = $_;
515
516 print "+ added .packlist dependency $base\n"
517 if $VERBOSE >= 3;
518 }
519
520 last;
521 }
522 }
523 }
524 }
525 }
526 }
527 }
528
529 #############################################################################
530
531 print "processing bundle files (try more -v power if you get bored waiting here)...\n"
532 if $VERBOSE >= 1;
533
534 my $data;
535 my @index;
536 my @order = sort {
537 length $a <=> length $b
538 or $a cmp $b
539 } keys %pm;
540
541 # sorting by name - better compression, but needs more metadata
542 # sorting by length - faster lookup
543 # usually, the metadata overhead beats the loss through compression
544
545 for my $pm (@order) {
546 my $path = $pm{$pm};
547
548 128 > length $pm
549 or die "ERROR: $pm: path too long (only 128 octets supported)\n";
550
551 my $src = ref $path
552 ? $$path
553 : do {
554 open my $pm, "<", $path
555 or die "$path: $!";
556
557 local $/;
558
559 <$pm>
560 };
561
562 my $size = length $src;
563
564 unless ($pmbin{$pm}) { # only do this unless the file is binary
565 if ($pm =~ /^auto\/POSIX\/[^\/]+\.al$/) {
566 if ($src =~ /^ unimpl \"/m) {
567 print "$pm: skipping (raises runtime error only).\n"
568 if $VERBOSE >= 3;
569 next;
570 }
571 }
572
573 $src = cache +($STRIP eq "ppi" ? "$UNISTRIP,$OPTIMISE_SIZE" : undef), $src, sub {
574 if ($UNISTRIP && $pm =~ /^unicore\/.*\.pl$/) {
575 print "applying unicore stripping $pm\n"
576 if $VERBOSE >= 6;
577
578 # special stripping for unicore swashes and properties
579 # much more could be done by going binary
580 $src =~ s{
581 (^return\ <<'END';\n) (.*?\n) (END(?:\n|\Z))
582 }{
583 my ($pre, $data, $post) = ($1, $2, $3);
584
585 for ($data) {
586 s/^([0-9a-fA-F]+)\t([0-9a-fA-F]+)\t/sprintf "%X\t%X", hex $1, hex $2/gem
587 if $OPTIMISE_SIZE;
588
589 # s{
590 # ^([0-9a-fA-F]+)\t([0-9a-fA-F]*)\t
591 # }{
592 # # ww - smaller filesize, UU - compress better
593 # pack "C0UU",
594 # hex $1,
595 # length $2 ? (hex $2) - (hex $1) : 0
596 # }gemx;
597
598 s/#.*\n/\n/mg;
599 s/\s+\n/\n/mg;
600 }
601
602 "$pre$data$post"
603 }smex;
604 }
605
606 if ($STRIP =~ /ppi/i) {
607 require PPI;
608
609 if (my $ppi = PPI::Document->new (\$src)) {
610 $ppi->prune ("PPI::Token::Comment");
611 $ppi->prune ("PPI::Token::Pod");
612
613 # prune END stuff
614 for (my $last = $ppi->last_element; $last; ) {
615 my $prev = $last->previous_token;
616
617 if ($last->isa (PPI::Token::Whitespace::)) {
618 $last->delete;
619 } elsif ($last->isa (PPI::Statement::End::)) {
620 $last->delete;
621 last;
622 } elsif ($last->isa (PPI::Token::Pod::)) {
623 $last->delete;
624 } else {
625 last;
626 }
627
628 $last = $prev;
629 }
630
631 # prune some but not all insignificant whitespace
632 for my $ws (@{ $ppi->find (PPI::Token::Whitespace::) }) {
633 my $prev = $ws->previous_token;
634 my $next = $ws->next_token;
635
636 if (!$prev || !$next) {
637 $ws->delete;
638 } else {
639 if (
640 $next->isa (PPI::Token::Operator::) && $next->{content} =~ /^(?:,|=|!|!=|==|=>)$/ # no ., because of digits. == float
641 or $prev->isa (PPI::Token::Operator::) && $prev->{content} =~ /^(?:,|=|\.|!|!=|==|=>)$/
642 or $prev->isa (PPI::Token::Structure::)
643 or ($OPTIMISE_SIZE &&
644 ($prev->isa (PPI::Token::Word::)
645 && (PPI::Token::Symbol:: eq ref $next
646 || $next->isa (PPI::Structure::Block::)
647 || $next->isa (PPI::Structure::List::)
648 || $next->isa (PPI::Structure::Condition::)))
649 )
650 ) {
651 $ws->delete;
652 } elsif ($prev->isa (PPI::Token::Whitespace::)) {
653 $ws->{content} = ' ';
654 $prev->delete;
655 } else {
656 $ws->{content} = ' ';
657 }
658 }
659 }
660
661 # prune whitespace around blocks
662 if ($OPTIMISE_SIZE) {
663 # these usually decrease size, but decrease compressability more
664 for my $struct (PPI::Structure::Block::, PPI::Structure::Condition::) {
665 for my $node (@{ $ppi->find ($struct) }) {
666 my $n1 = $node->first_token;
667 my $n2 = $n1->previous_token;
668 $n1->delete if $n1->isa (PPI::Token::Whitespace::);
669 $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
670 my $n1 = $node->last_token;
671 my $n2 = $n1->next_token;
672 $n1->delete if $n1->isa (PPI::Token::Whitespace::);
673 $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
674 }
675 }
676
677 for my $node (@{ $ppi->find (PPI::Structure::List::) }) {
678 my $n1 = $node->first_token;
679 $n1->delete if $n1->isa (PPI::Token::Whitespace::);
680 my $n1 = $node->last_token;
681 $n1->delete if $n1->isa (PPI::Token::Whitespace::);
682 }
683 }
684
685 # reformat qw() lists which often have lots of whitespace
686 for my $node (@{ $ppi->find (PPI::Token::QuoteLike::Words::) }) {
687 if ($node->{content} =~ /^qw(.)(.*)(.)$/s) {
688 my ($a, $qw, $b) = ($1, $2, $3);
689 $qw =~ s/^\s+//;
690 $qw =~ s/\s+$//;
691 $qw =~ s/\s+/ /g;
692 $node->{content} = "qw$a$qw$b";
693 }
694 }
695
696 $src = $ppi->serialize;
697 } else {
698 warn "WARNING: $pm{$pm}: PPI failed to parse this file\n";
699 }
700 } elsif ($STRIP =~ /pod/i && $pm ne "Opcode.pm") { # opcode parses its own pod
701 require Pod::Strip;
702
703 my $stripper = Pod::Strip->new;
704
705 my $out;
706 $stripper->output_string (\$out);
707 $stripper->parse_string_document ($src)
708 or die;
709 $src = $out;
710 }
711
712 if ($VERIFY && $pm =~ /\.pm$/ && $pm ne "Opcode.pm") {
713 if (open my $fh, "-|") {
714 <$fh>;
715 } else {
716 eval "#line 1 \"$pm\"\n$src" or warn "\n\n\n$pm\n\n$src\n$@\n\n\n";
717 exit 0;
718 }
719 }
720
721 $src
722 };
723
724 # if ($pm eq "Opcode.pm") {
725 # open my $fh, ">x" or die; print $fh $src;#d#
726 # exit 1;
727 # }
728 }
729
730 print "adding $pm (original size $size, stored size ", length $src, ")\n"
731 if $VERBOSE >= 2;
732
733 push @index, ((length $pm) << 25) | length $data;
734 $data .= $pm . $src;
735 }
736
737 length $data < 2**25
738 or die "ERROR: bundle too large (only 32MB supported)\n";
739
740 my $varpfx = "bundle";
741
742 #############################################################################
743 # output
744
745 print "generating $PREFIX.h... "
746 if $VERBOSE >= 1;
747
748 {
749 open my $fh, ">", "$PREFIX.h"
750 or die "$PREFIX.h: $!\n";
751
752 print $fh <<EOF;
753 /* do not edit, automatically created by staticperl */
754
755 #include <EXTERN.h>
756 #include <perl.h>
757 #include <XSUB.h>
758
759 /* public API */
760 EXTERN_C PerlInterpreter *staticperl;
761 EXTERN_C void staticperl_xs_init (pTHX);
762 EXTERN_C void staticperl_init (XSINIT_t xs_init); /* argument can be 0 */
763 EXTERN_C void staticperl_cleanup (void);
764
765 EOF
766 }
767
768 print "\n"
769 if $VERBOSE >= 1;
770
771 #############################################################################
772 # output
773
774 print "generating $PREFIX.c... "
775 if $VERBOSE >= 1;
776
777 open my $fh, ">", "$PREFIX.c"
778 or die "$PREFIX.c: $!\n";
779
780 print $fh <<EOF;
781 /* do not edit, automatically created by staticperl */
782
783 #include "bundle.h"
784
785 /* public API */
786 PerlInterpreter *staticperl;
787
788 EOF
789
790 #############################################################################
791 # bundle data
792
793 my $count = @index;
794
795 print $fh <<EOF;
796 #include "bundle.h"
797
798 /* bundle data */
799
800 static const U32 $varpfx\_count = $count;
801 static const U32 $varpfx\_index [$count + 1] = {
802 EOF
803
804 my $col;
805 for (@index) {
806 printf $fh "0x%08x,", $_;
807 print $fh "\n" unless ++$col % 10;
808
809 }
810 printf $fh "0x%08x\n};\n", (length $data);
811
812 print $fh "static const char $varpfx\_data [] =\n";
813 dump_string $fh, $data;
814
815 print $fh ";\n\n";
816
817 #############################################################################
818 # bootstrap
819
820 # boot file for staticperl
821 # this file will be eval'ed at initialisation time
822
823 # lines marked with "^D" are only used when $HAVE_DLLS
824 my $bootstrap = '
825 BEGIN {
826 package ' . $PACKAGE . ';
827
828 # the path prefix to use when putting files into %INC
829 our $inc_prefix;
830
831 # the @INC hook to use when we have PerlIO::scalar available
832 my $perlio_inc = sub {
833 my $data = find "$_[1]"
834 or return;
835
836 $INC{$_[1]} = "$inc_prefix$_[1]";
837
838 open my $fh, "<", \$data;
839 $fh
840 };
841
842 D if (defined &PerlIO::scalar::bootstrap) {
843 # PerlIO::scalar statically compiled in
844 PerlIO::scalar->bootstrap;
845 @INC = $perlio_inc;
846 D } else {
847 D # PerlIO::scalar not available, use slower method
848 D @INC = sub {
849 D # always check if PerlIO::scalar might now be available
850 D if (defined &PerlIO::scalar::bootstrap) {
851 D # switch to the faster perlio_inc hook
852 D @INC = map { $_ == $_[0] ? $perlio_inc : $_ } @INC;
853 D goto &$perlio_inc;
854 D }
855 D
856 D my $data = find "$_[1]"
857 D or return;
858 D
859 D $INC{$_[1]} = "$inc_prefix$_[1]";
860 D
861 D sub {
862 D $data =~ /\G([^\n]*\n?)/g
863 D or return;
864 D
865 D $_ = $1;
866 D 1
867 D }
868 D };
869 D }
870 }
871 ';
872
873 $bootstrap .= "require '&&boot';"
874 if exists $pm{"&&boot"};
875
876 if ($HAVE_DLLS) {
877 $bootstrap =~ s/^D/ /mg;
878 } else {
879 $bootstrap =~ s/^D.*$//mg;
880 }
881
882 $bootstrap =~ s/#.*$//mg;
883 $bootstrap =~ s/\s+/ /g;
884 $bootstrap =~ s/(\W) /$1/g;
885 $bootstrap =~ s/ (\W)/$1/g;
886
887 print $fh "const char bootstrap [] = ";
888 dump_string $fh, $bootstrap;
889 print $fh ";\n\n";
890
891 print $fh <<EOF;
892 /* search all bundles for the given file, using binary search */
893 XS(find)
894 {
895 dXSARGS;
896
897 if (items != 1)
898 Perl_croak (aTHX_ "Usage: $PACKAGE\::find (\$path)");
899
900 {
901 STRLEN namelen;
902 char *name = SvPV (ST (0), namelen);
903 SV *res = 0;
904
905 int l = 0, r = $varpfx\_count;
906
907 while (l <= r)
908 {
909 int m = (l + r) >> 1;
910 U32 idx = $varpfx\_index [m];
911 int comp = namelen - (idx >> 25);
912
913 if (!comp)
914 {
915 int ofs = idx & 0x1FFFFFFU;
916 comp = memcmp (name, $varpfx\_data + ofs, namelen);
917
918 if (!comp)
919 {
920 /* found */
921 int ofs2 = $varpfx\_index [m + 1] & 0x1FFFFFFU;
922
923 ofs += namelen;
924 res = newSVpvn ($varpfx\_data + ofs, ofs2 - ofs);
925 goto found;
926 }
927 }
928
929 if (comp < 0)
930 r = m - 1;
931 else
932 l = m + 1;
933 }
934
935 XSRETURN (0);
936
937 found:
938 ST (0) = sv_2mortal (res);
939 }
940
941 XSRETURN (1);
942 }
943
944 /* list all files in the bundle */
945 XS(list)
946 {
947 dXSARGS;
948
949 if (items != 0)
950 Perl_croak (aTHX_ "Usage: $PACKAGE\::list");
951
952 {
953 int i;
954
955 EXTEND (SP, $varpfx\_count);
956
957 for (i = 0; i < $varpfx\_count; ++i)
958 {
959 U32 idx = $varpfx\_index [i];
960
961 PUSHs (sv_2mortal (newSVpvn ($varpfx\_data + (idx & 0x1FFFFFFU), idx >> 25)));
962 }
963 }
964
965 XSRETURN ($varpfx\_count);
966 }
967
968 EOF
969
970 #############################################################################
971 # xs_init
972
973 print $fh <<EOF;
974 void
975 staticperl_xs_init (pTHX)
976 {
977 EOF
978
979 @static_ext = sort @static_ext;
980
981 # prototypes
982 for (@static_ext) {
983 s/\.pm$//;
984 (my $cname = $_) =~ s/\//__/g;
985 print $fh " EXTERN_C void boot_$cname (pTHX_ CV* cv);\n";
986 }
987
988 print $fh <<EOF;
989 char *file = __FILE__;
990 dXSUB_SYS;
991
992 newXSproto ("$PACKAGE\::find", find, file, "\$");
993 newXSproto ("$PACKAGE\::list", list, file, "");
994 EOF
995
996 # calls
997 for (@static_ext) {
998 s/\.pm$//;
999
1000 (my $cname = $_) =~ s/\//__/g;
1001 (my $pname = $_) =~ s/\//::/g;
1002
1003 my $bootstrap = $pname eq "DynaLoader" ? "boot_DynaLoader" : "bootstrap";
1004
1005 print $fh " newXS (\"$pname\::$bootstrap\", boot_$cname, file);\n";
1006 }
1007
1008 print $fh <<EOF;
1009 Perl_av_create_and_unshift_one (&PL_preambleav, newSVpv (bootstrap, sizeof (bootstrap) - 1));
1010
1011 if (PL_oldname)
1012 ((XSINIT_t)PL_oldname)(aTHX);
1013 }
1014 EOF
1015
1016 #############################################################################
1017 # optional perl_init/perl_destroy
1018
1019 if ($IGNORE_ENV) {
1020 $IGNORE_ENV = <<EOF;
1021 unsetenv ("PERL_UNICODE");
1022 unsetenv ("PERL_HASH_SEED_DEBUG");
1023 unsetenv ("PERL_DESTRUCT_LEVEL");
1024 unsetenv ("PERL_SIGNALS");
1025 unsetenv ("PERL_DEBUG_MSTATS");
1026 unsetenv ("PERL5OPT");
1027 unsetenv ("PERLIO_DEBUG");
1028 unsetenv ("PERLIO");
1029 unsetenv ("PERL_HASH_SEED");
1030 EOF
1031 } else {
1032 $IGNORE_ENV = "";
1033 }
1034
1035 if ($APP) {
1036 print $fh <<EOF;
1037
1038 int
1039 main (int argc, char *argv [])
1040 {
1041 extern char **environ;
1042 int i, exitstatus;
1043 char **args = malloc ((argc + 3) * sizeof (const char *));
1044
1045 args [0] = argv [0];
1046 args [1] = "-e";
1047 args [2] = "0";
1048 args [3] = "--";
1049
1050 for (i = 1; i < argc; ++i)
1051 args [i + 3] = argv [i];
1052
1053 $IGNORE_ENV
1054 PERL_SYS_INIT3 (&argc, &argv, &environ);
1055 staticperl = perl_alloc ();
1056 perl_construct (staticperl);
1057
1058 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1059
1060 exitstatus = perl_parse (staticperl, staticperl_xs_init, argc + 3, args, environ);
1061 free (args);
1062 if (!exitstatus)
1063 perl_run (staticperl);
1064
1065 exitstatus = perl_destruct (staticperl);
1066 perl_free (staticperl);
1067 PERL_SYS_TERM ();
1068
1069 return exitstatus;
1070 }
1071 EOF
1072 } elsif ($PERL) {
1073 print $fh <<EOF;
1074
1075 int
1076 main (int argc, char *argv [])
1077 {
1078 extern char **environ;
1079 int exitstatus;
1080
1081 $IGNORE_ENV
1082 PERL_SYS_INIT3 (&argc, &argv, &environ);
1083 staticperl = perl_alloc ();
1084 perl_construct (staticperl);
1085
1086 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1087
1088 exitstatus = perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
1089 if (!exitstatus)
1090 perl_run (staticperl);
1091
1092 exitstatus = perl_destruct (staticperl);
1093 perl_free (staticperl);
1094 PERL_SYS_TERM ();
1095
1096 return exitstatus;
1097 }
1098 EOF
1099 } else {
1100 print $fh <<EOF;
1101
1102 EXTERN_C void
1103 staticperl_init (XSINIT_t xs_init)
1104 {
1105 static char *args[] = {
1106 "staticperl",
1107 "-e",
1108 "0"
1109 };
1110
1111 extern char **environ;
1112 int argc = sizeof (args) / sizeof (args [0]);
1113 char **argv = args;
1114
1115 $IGNORE_ENV
1116 PERL_SYS_INIT3 (&argc, &argv, &environ);
1117 staticperl = perl_alloc ();
1118 perl_construct (staticperl);
1119 PL_origalen = 1;
1120 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1121 PL_oldname = (char *)xs_init;
1122 perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
1123
1124 perl_run (staticperl);
1125 }
1126
1127 EXTERN_C void
1128 staticperl_cleanup (void)
1129 {
1130 perl_destruct (staticperl);
1131 perl_free (staticperl);
1132 staticperl = 0;
1133 PERL_SYS_TERM ();
1134 }
1135 EOF
1136 }
1137
1138 print -s "$PREFIX.c", " octets (", (length $data) , " data octets).\n\n"
1139 if $VERBOSE >= 1;
1140
1141 #############################################################################
1142 # libs, cflags
1143
1144 {
1145 print "generating $PREFIX.ccopts... "
1146 if $VERBOSE >= 1;
1147
1148 my $str = "$Config{ccflags} $Config{optimize} $Config{cppflags} -I$Config{archlibexp}/CORE";
1149 $str =~ s/([\(\)])/\\$1/g;
1150
1151 open my $fh, ">$PREFIX.ccopts"
1152 or die "$PREFIX.ccopts: $!";
1153 print $fh $str;
1154
1155 print "$str\n\n"
1156 if $VERBOSE >= 1;
1157 }
1158
1159 {
1160 print "generating $PREFIX.ldopts... ";
1161
1162 my $str = $STATIC ? "-static " : "";
1163
1164 $str .= "$Config{ccdlflags} $Config{ldflags} @libs $Config{archlibexp}/CORE/$Config{libperl} $Config{perllibs}";
1165
1166 my %seen;
1167 $str .= " $_" for grep !$seen{$_}++, ($extralibs =~ /(\S+)/g);
1168
1169 for (@staticlibs) {
1170 $str =~ s/(^|\s) (-l\Q$_\E) ($|\s)/$1-Wl,-Bstatic $2 -Wl,-Bdynamic$3/gx;
1171 }
1172
1173 $str =~ s/([\(\)])/\\$1/g;
1174
1175 open my $fh, ">$PREFIX.ldopts"
1176 or die "$PREFIX.ldopts: $!";
1177 print $fh $str;
1178
1179 print "$str\n\n"
1180 if $VERBOSE >= 1;
1181 }
1182
1183 if ($PERL or defined $APP) {
1184 $APP = "perl" unless defined $APP;
1185
1186 print "building $APP...\n"
1187 if $VERBOSE >= 1;
1188
1189 system "$Config{cc} \$(cat bundle.ccopts\) -o \Q$APP\E bundle.c \$(cat bundle.ldopts\)";
1190
1191 unlink "$PREFIX.$_"
1192 for qw(ccopts ldopts c h);
1193
1194 print "\n"
1195 if $VERBOSE >= 1;
1196 }
1197