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