ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/App-Staticperl/mkbundle
Revision: 1.38
Committed: Tue Feb 15 18:37:34 2022 UTC (2 years, 3 months ago) by root
Branch: MAIN
CVS Tags: rel-1_46
Changes since 1.37: +1 -1 lines
Log Message:
1.46

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