ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/App-Staticperl/mkbundle
Revision: 1.39
Committed: Thu Aug 3 02:48:48 2023 UTC (10 months, 4 weeks ago) by root
Branch: MAIN
Changes since 1.38: +14 -5 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_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 = 2; # 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 while ($next->isa (PPI::Token::Whitespace::)) {
661 $next->delete;
662 $next = $ws->next_token;
663 }
664
665 if (
666 $next->isa (PPI::Token::Operator::) && $next->{content} =~ /^(?:,|=|!|!=|==|=>)$/ # no ., because of digits. == float
667 or $prev->isa (PPI::Token::Operator::) && $prev->{content} =~ /^(?:,|=|\.|!|!=|==|=>)$/
668 or $prev->isa (PPI::Token::Structure::)
669 or ($OPTIMISE_SIZE &&
670 ($prev->isa (PPI::Token::Word::)
671 && (PPI::Token::Symbol:: eq ref $next
672 || $next->isa (PPI::Structure::Block::)
673 || $next->isa (PPI::Structure::List::)
674 || $next->isa (PPI::Structure::Condition::)))
675 )
676 ) {
677 # perl has some idiotic warnigns about nonexisting operators
678 if ($prev->isa (PPI::Token::Operator::) && $prev->{content} eq "="
679 && $next->isa (PPI::Token::Operator::) && $next->{content} =~ /[+\-]/
680 ) {
681 # avoid "Reverse %s operator" diagnostic
682 } else {
683 $ws->delete;
684 }
685 } else {
686 $ws->{content} = ' ';
687 }
688 }
689 }
690
691 # prune whitespace around blocks
692 if ($OPTIMISE_SIZE) {
693 # these usually decrease size, but decrease compressability more
694 for my $struct (PPI::Structure::Block::, PPI::Structure::Condition::) {
695 for my $node (@{ $ppi->find ($struct) }) {
696 my $n1 = $node->first_token;
697 my $n2 = $n1->previous_token;
698 $n1->delete if $n1->isa (PPI::Token::Whitespace::);
699 $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
700 my $n1 = $node->last_token;
701 my $n2 = $n1->next_token;
702 $n1->delete if $n1->isa (PPI::Token::Whitespace::);
703 $n2->delete if $n2 && $n2->isa (PPI::Token::Whitespace::);
704 }
705 }
706
707 for my $node (@{ $ppi->find (PPI::Structure::List::) }) {
708 my $n1 = $node->first_token;
709 $n1->delete if $n1->isa (PPI::Token::Whitespace::);
710 my $n1 = $node->last_token;
711 $n1->delete if $n1->isa (PPI::Token::Whitespace::);
712 }
713 }
714
715 # reformat qw() lists which often have lots of whitespace
716 for my $node (@{ $ppi->find (PPI::Token::QuoteLike::Words::) }) {
717 if ($node->{content} =~ /^qw(.)(.*)(.)$/s) {
718 my ($a, $qw, $b) = ($1, $2, $3);
719 $qw =~ s/^\s+//;
720 $qw =~ s/\s+$//;
721 $qw =~ s/\s+/ /g;
722 $node->{content} = "qw$a$qw$b";
723 }
724 }
725
726 $src = $ppi->serialize;
727 } else {
728 warn "WARNING: $pm{$pm}: PPI failed to parse this file\n";
729 }
730 } elsif ($STRIP =~ /pod/i && $pm ne "Opcode.pm") { # opcode parses its own pod
731 require Pod::Strip;
732
733 my $stripper = Pod::Strip->new;
734
735 my $out;
736 $stripper->output_string (\$out);
737 $stripper->parse_string_document ($src)
738 or die;
739 $src = $out;
740 }
741
742 if ($VERIFY && $pm =~ /\.pm$/ && $pm ne "Opcode.pm") {
743 if (open my $fh, "-|") {
744 <$fh>;
745 } else {
746 eval "#line 1 \"$pm\"\n$src" or warn "\n\n\n$pm\n\n$src\n$@\n\n\n";
747 exit 0;
748 }
749 }
750
751 $src
752 };
753
754 # if ($pm eq "Opcode.pm") {
755 # open my $fh, ">x" or die; print $fh $src;#d#
756 # exit 1;
757 # }
758 }
759
760 print "adding $pm (original size $size, stored size ", length $src, ")\n"
761 if $VERBOSE >= 2;
762
763 push @index, ((length $pm) << 25) | length $data;
764 $data .= $pm . $src;
765 }
766
767 length $data < 2**25
768 or die "ERROR: bundle too large (only 32MB supported)\n";
769
770 my $varpfx = "bundle";
771
772 #############################################################################
773 # output
774
775 print "generating $PREFIX.h... "
776 if $VERBOSE >= 1;
777
778 {
779 open my $fh, ">", "$PREFIX.h"
780 or die "$PREFIX.h: $!\n";
781
782 print $fh <<EOF;
783 /* do not edit, automatically created by staticperl */
784
785 #include <EXTERN.h>
786 #include <perl.h>
787 #include <XSUB.h>
788
789 /* public API */
790 EXTERN_C PerlInterpreter *staticperl;
791 EXTERN_C void staticperl_xs_init (pTHX);
792 EXTERN_C void staticperl_init (XSINIT_t xs_init); /* argument can be 0 */
793 EXTERN_C void staticperl_cleanup (void);
794
795 EOF
796 }
797
798 print "\n"
799 if $VERBOSE >= 1;
800
801 #############################################################################
802 # output
803
804 print "generating $PREFIX.c... "
805 if $VERBOSE >= 1;
806
807 open my $fh, ">", "$PREFIX.c"
808 or die "$PREFIX.c: $!\n";
809
810 print $fh <<EOF;
811 /* do not edit, automatically created by staticperl */
812
813 #include "bundle.h"
814
815 /* public API */
816 PerlInterpreter *staticperl;
817
818 EOF
819
820 #############################################################################
821 # bundle data
822
823 my $count = @index;
824
825 print $fh <<EOF;
826 #include "bundle.h"
827
828 /* bundle data */
829
830 static const U32 $varpfx\_count = $count;
831 static const U32 $varpfx\_index [$count + 1] = {
832 EOF
833
834 my $col;
835 for (@index) {
836 printf $fh "0x%08x,", $_;
837 print $fh "\n" unless ++$col % 10;
838
839 }
840 printf $fh "0x%08x\n};\n", (length $data);
841
842 print $fh "static const char $varpfx\_data [] =\n";
843 dump_string $fh, $data;
844
845 print $fh ";\n\n";
846
847 #############################################################################
848 # bootstrap
849
850 # boot file for staticperl
851 # this file will be eval'ed at initialisation time
852
853 # lines marked with "^D" are only used when $HAVE_DYNAMIC
854 my $bootstrap = '
855 BEGIN {
856 package ' . $PACKAGE . ';
857
858 # the path prefix to use when putting files into %INC
859 our $inc_prefix;
860
861 # the @INC hook to use when we have PerlIO::scalar available
862 my $perlio_inc = sub {
863 my $data = find "$_[1]"
864 or return;
865
866 $INC{$_[1]} = "$inc_prefix$_[1]";
867
868 open my $fh, "<", \$data;
869 $fh
870 };
871
872 D if (defined &PerlIO::scalar::bootstrap) {
873 # PerlIO::scalar statically compiled in
874 PerlIO::scalar->bootstrap;
875 @INC = $perlio_inc;
876 D } else {
877 D # PerlIO::scalar not available, use slower method
878 D @INC = sub {
879 D # always check if PerlIO::scalar might now be available
880 D if (defined &PerlIO::scalar::bootstrap) {
881 D # switch to the faster perlio_inc hook
882 D @INC = map { $_ == $_[0] ? $perlio_inc : $_ } @INC;
883 D goto &$perlio_inc;
884 D }
885 D
886 D my $data = find "$_[1]"
887 D or return;
888 D
889 D $INC{$_[1]} = "$inc_prefix$_[1]";
890 D
891 D sub {
892 D $data =~ /\G([^\n]*\n?)/g
893 D or return;
894 D
895 D $_ = $1;
896 D 1
897 D }
898 D };
899 D }
900 }
901 ';
902
903 $bootstrap .= "require '!boot';"
904 if exists $pm{"!boot"};
905
906 if ($HAVE_DYNAMIC) {
907 $bootstrap =~ s/^D/ /mg;
908 } else {
909 $bootstrap =~ s/^D.*$//mg;
910 }
911
912 $bootstrap =~ s/#.*$//mg;
913 $bootstrap =~ s/\s+/ /g;
914 $bootstrap =~ s/(\W) /$1/g;
915 $bootstrap =~ s/ (\W)/$1/g;
916
917 print $fh "const char bootstrap [] = ";
918 dump_string $fh, $bootstrap;
919 print $fh ";\n\n";
920
921 print $fh <<EOF;
922 /* search all bundles for the given file, using binary search */
923 XS(find)
924 {
925 dXSARGS;
926
927 if (items != 1)
928 Perl_croak (aTHX_ "Usage: $PACKAGE\::find (\$path)");
929
930 {
931 STRLEN namelen;
932 char *name = SvPV (ST (0), namelen);
933 SV *res = 0;
934
935 int l = 0, r = $varpfx\_count;
936
937 while (l <= r)
938 {
939 int m = (l + r) >> 1;
940 U32 idx = $varpfx\_index [m];
941 int comp = namelen - (idx >> 25);
942
943 if (!comp)
944 {
945 int ofs = idx & 0x1FFFFFFU;
946 comp = memcmp (name, $varpfx\_data + ofs, namelen);
947
948 if (!comp)
949 {
950 /* found */
951 int ofs2 = $varpfx\_index [m + 1] & 0x1FFFFFFU;
952
953 ofs += namelen;
954 res = newSVpvn ($varpfx\_data + ofs, ofs2 - ofs);
955 goto found;
956 }
957 }
958
959 if (comp < 0)
960 r = m - 1;
961 else
962 l = m + 1;
963 }
964
965 XSRETURN (0);
966
967 found:
968 ST (0) = sv_2mortal (res);
969 }
970
971 XSRETURN (1);
972 }
973
974 /* list all files in the bundle */
975 XS(list)
976 {
977 dXSARGS;
978
979 if (items != 0)
980 Perl_croak (aTHX_ "Usage: $PACKAGE\::list");
981
982 {
983 int i;
984
985 EXTEND (SP, $varpfx\_count);
986
987 for (i = 0; i < $varpfx\_count; ++i)
988 {
989 U32 idx = $varpfx\_index [i];
990
991 PUSHs (sv_2mortal (newSVpvn ($varpfx\_data + (idx & 0x1FFFFFFU), idx >> 25)));
992 }
993 }
994
995 XSRETURN ($varpfx\_count);
996 }
997
998 #ifdef STATICPERL_BUNDLE_INCLUDE
999 #include STATICPERL_BUNDLE_INCLUDE
1000 #endif
1001
1002 EOF
1003
1004 #############################################################################
1005 # xs_init
1006
1007 print $fh <<EOF;
1008 void
1009 staticperl_xs_init (pTHX)
1010 {
1011 EOF
1012
1013 @static_ext = sort @static_ext;
1014
1015 # prototypes
1016 for (@static_ext) {
1017 s/\.pm$//;
1018 (my $cname = $_) =~ s/\//__/g;
1019 print $fh " EXTERN_C void boot_$cname (pTHX_ CV* cv);\n";
1020 }
1021
1022 print $fh <<EOF;
1023 char *file = __FILE__;
1024 dXSUB_SYS;
1025
1026 newXSproto ("$PACKAGE\::find", find, file, "\$");
1027 newXSproto ("$PACKAGE\::list", list, file, "");
1028
1029 #ifdef STATICPERL_BUNDLE_XS_INIT
1030 STATICPERL_BUNDLE_XS_INIT;
1031 #endif
1032 EOF
1033
1034 # calls
1035 for (@static_ext) {
1036 s/\.pm$//;
1037
1038 (my $cname = $_) =~ s/\//__/g;
1039 (my $pname = $_) =~ s/\//::/g;
1040
1041 my $bootstrap = $pname eq "DynaLoader" ? "boot_DynaLoader" : "bootstrap";
1042
1043 print $fh " newXS (\"$pname\::$bootstrap\", boot_$cname, file);\n";
1044 }
1045
1046 print $fh <<EOF;
1047 Safefree (PL_origfilename);
1048 PL_origfilename = savepv (PL_origargv [0]);
1049 sv_setpv (GvSV (gv_fetchpvs ("0", GV_ADD|GV_NOTQUAL, SVt_PV)), PL_origfilename);
1050
1051 #ifdef _WIN32
1052 /* windows perls usually trail behind unix perls 8-10 years in exporting symbols */
1053
1054 if (!PL_preambleav)
1055 PL_preambleav = newAV ();
1056
1057 av_unshift (PL_preambleav, 1);
1058 av_store (PL_preambleav, 0, newSVpv (bootstrap, sizeof (bootstrap) - 1));
1059 #else
1060 Perl_av_create_and_unshift_one (&PL_preambleav, newSVpv (bootstrap, sizeof (bootstrap) - 1));
1061 #endif
1062
1063 if (PL_oldname)
1064 ((XSINIT_t)PL_oldname)(aTHX);
1065 }
1066 EOF
1067
1068 #############################################################################
1069 # optional perl_init/perl_destroy
1070
1071 if ($IGNORE_ENV) {
1072 $IGNORE_ENV = <<EOF;
1073 unsetenv ("PERL_UNICODE");
1074 unsetenv ("PERL_HASH_SEED_DEBUG");
1075 unsetenv ("PERL_DESTRUCT_LEVEL");
1076 unsetenv ("PERL_SIGNALS");
1077 unsetenv ("PERL_DEBUG_MSTATS");
1078 unsetenv ("PERL5OPT");
1079 unsetenv ("PERLIO_DEBUG");
1080 unsetenv ("PERLIO");
1081 unsetenv ("PERL_HASH_SEED");
1082 EOF
1083 } else {
1084 $IGNORE_ENV = "";
1085 }
1086
1087 if ($APP) {
1088 print $fh <<EOF;
1089
1090 int
1091 main (int argc, char *argv [])
1092 {
1093 extern char **environ;
1094 int i, exitstatus;
1095 char **args = malloc ((argc + 3) * sizeof (const char *));
1096
1097 args [0] = argv [0];
1098 args [1] = "-e";
1099 args [2] = "0";
1100 args [3] = "--";
1101
1102 for (i = 1; i < argc; ++i)
1103 args [i + 3] = argv [i];
1104
1105 $IGNORE_ENV
1106 PERL_SYS_INIT3 (&argc, &argv, &environ);
1107 staticperl = perl_alloc ();
1108 perl_construct (staticperl);
1109
1110 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1111
1112 exitstatus = perl_parse (staticperl, staticperl_xs_init, argc + 3, args, environ);
1113 if (!exitstatus)
1114 perl_run (staticperl);
1115
1116 exitstatus = perl_destruct (staticperl);
1117 perl_free (staticperl);
1118 PERL_SYS_TERM ();
1119 /*free (args); no point doing it this late */
1120
1121 return exitstatus;
1122 }
1123 EOF
1124 } elsif ($PERL) {
1125 print $fh <<EOF;
1126
1127 int
1128 main (int argc, char *argv [])
1129 {
1130 extern char **environ;
1131 int exitstatus;
1132
1133 $IGNORE_ENV
1134 PERL_SYS_INIT3 (&argc, &argv, &environ);
1135 staticperl = perl_alloc ();
1136 perl_construct (staticperl);
1137
1138 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1139
1140 exitstatus = perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
1141 if (!exitstatus)
1142 perl_run (staticperl);
1143
1144 exitstatus = perl_destruct (staticperl);
1145 perl_free (staticperl);
1146 PERL_SYS_TERM ();
1147
1148 return exitstatus;
1149 }
1150 EOF
1151 } else {
1152 print $fh <<EOF;
1153
1154 EXTERN_C void
1155 staticperl_init (XSINIT_t xs_init)
1156 {
1157 static char *args[] = {
1158 "staticperl",
1159 "-e",
1160 "0"
1161 };
1162
1163 extern char **environ;
1164 int argc = sizeof (args) / sizeof (args [0]);
1165 char **argv = args;
1166
1167 $IGNORE_ENV
1168 PERL_SYS_INIT3 (&argc, &argv, &environ);
1169 staticperl = perl_alloc ();
1170 perl_construct (staticperl);
1171 PL_origalen = 1;
1172 PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
1173 PL_oldname = (char *)xs_init;
1174 perl_parse (staticperl, staticperl_xs_init, argc, argv, environ);
1175
1176 perl_run (staticperl);
1177 }
1178
1179 EXTERN_C void
1180 staticperl_cleanup (void)
1181 {
1182 perl_destruct (staticperl);
1183 perl_free (staticperl);
1184 staticperl = 0;
1185 PERL_SYS_TERM ();
1186 }
1187 EOF
1188 }
1189
1190 close $fh;
1191
1192 print -s "$PREFIX.c", " octets (", (length $data) , " data octets).\n\n"
1193 if $VERBOSE >= 1;
1194
1195 #############################################################################
1196 # libs, cflags
1197
1198 my $ccopts;
1199
1200 {
1201 print "generating $PREFIX.ccopts... "
1202 if $VERBOSE >= 1;
1203
1204 $ccopts = "$Config{ccflags} $Config{optimize} $Config{cppflags} -I$Config{archlibexp}/CORE $EXTRA_CFLAGS";
1205 $ccopts =~ s/([\(\)])/\\$1/g;
1206
1207 open my $fh, ">$PREFIX.ccopts"
1208 or die "$PREFIX.ccopts: $!";
1209 print $fh $ccopts;
1210
1211 print "$ccopts\n\n"
1212 if $VERBOSE >= 1;
1213 }
1214
1215 my $ldopts;
1216
1217 {
1218 print "generating $PREFIX.ldopts... ";
1219
1220 $ldopts = $STATIC ? "-static " : "";
1221
1222 $ldopts .= "$Config{ccdlflags} $Config{ldflags} $EXTRA_LDFLAGS @libs $Config{archlibexp}/CORE/$Config{libperl} $Config{perllibs} $EXTRA_LIBS";
1223
1224 my %seen;
1225 $ldopts .= " $_" for reverse grep !$seen{$_}++, reverse +($extralibs =~ /(\S+)/g);
1226
1227 for (@staticlibs) {
1228 $ldopts =~ s/(^|\s) (-l\Q$_\E) ($|\s)/$1-Wl,-Bstatic $2 -Wl,-Bdynamic$3/gx;
1229 }
1230
1231 $ldopts =~ s/([\(\)])/\\$1/g;
1232
1233 open my $fh, ">$PREFIX.ldopts"
1234 or die "$PREFIX.ldopts: $!";
1235 print $fh $ldopts;
1236
1237 print "$ldopts\n\n"
1238 if $VERBOSE >= 1;
1239 }
1240
1241 if ($PERL or defined $APP) {
1242 $APP = "perl" unless defined $APP;
1243
1244 my $build = "$Config{cc} $ccopts -o \Q$APP\E$Config{_exe} bundle.c $ldopts";
1245
1246 print "build $APP...\n"
1247 if $VERBOSE >= 1;
1248
1249 print "$build\n"
1250 if $VERBOSE >= 2;
1251
1252 system $build;
1253
1254 unlink "$PREFIX.$_"
1255 for qw(ccopts ldopts c h);
1256
1257 print "\n"
1258 if $VERBOSE >= 1;
1259 }
1260