ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/App-Staticperl/mkbundle
Revision: 1.43
Committed: Mon Aug 7 04:36:34 2023 UTC (3 years, 1 month ago) by root
Branch: MAIN
Changes since 1.42: +14 -9 lines
Log Message:
*** empty log message ***

File Contents

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