Symbian/beginnings of Series 80 support
[p5sagit/p5-mst-13.2.git] / symbian / xsbuild.pl
1 #!/usr/bin/perl -w
2
3 use strict;
4
5 use Getopt::Long;
6 use File::Basename;
7 use Cwd;
8
9 do "sanity.pl";
10
11 my $CoreBuild = -d "ext" && -f "perl.h" && -d "symbian" && -f "perl.c";
12
13 my $SymbianVersion = $ENV{XSBUILD_SYMBIAN_VERSION};
14 my $PerlVersion    = $ENV{XSBUILD_PERL_VERSION};
15 my $CSuffix        = '.c';
16 my $CPlusPlus;
17 my $Config;
18 my $Build;
19 my $Clean;
20 my $DistClean;
21 my $Sis;
22
23 sub usage {
24     die <<__EOF__;
25 $0: Usage: $0 [--symbian=version] [--perl=version]
26               [--csuffix=csuffix] [--cplusplus]
27               [--win=win] [--arm=arm]
28               [--config|--build|--clean|--distclean|--sis] ext
29 __EOF__
30 }
31
32 my $CWD;
33 my $SDK;
34 my $VERSION;
35 my $R_V_SV;
36 my $PERLSDK;
37 my $WIN = 'wins';
38 my $ARM = 'thumb';
39 my $BUILDROOT = getcwd();
40
41 if ( !defined $PerlVersion && $0 =~ m:\\symbian\\perl\\(.+)\\bin\\xsbuild.pl:i )
42 {
43     $PerlVersion = $1;
44 }
45
46 if ( !defined $SymbianVersion) {
47     ($SymbianVersion) = ($ENV{PATH} =~ m!C:\\Symbian\\(.+?)\\!i);
48 }
49
50 my $S60SDK;
51
52 my ($SYMBIAN_ROOT, $SYMBIAN_VERSION, $SDK_NAME, $SDK_VARIANT, $SDK_VERSION);
53
54 if ($CoreBuild) {
55     unshift @INC, "symbian";
56     do "sanity.pl";
57     my %VERSION = %{ do "version.pl" };
58     ($SYMBIAN_ROOT, $SYMBIAN_VERSION, $SDK_NAME, $SDK_VARIANT, $SDK_VERSION) =
59       @{ do "sdk.pl" };
60     $VERSION = "$VERSION{REVISION}$VERSION{VERSION}$VERSION{SUBVERSION}";
61     $R_V_SV  = "$VERSION{REVISION}.$VERSION{VERSION}.$VERSION{SUBVERSION}";
62     $BUILDROOT    = do "cwd.pl";
63     $PerlVersion    = $R_V_SV;
64     $S60SDK = $ENV{S60SDK}; # from sdk.pl
65 }
66
67 usage()
68   unless GetOptions(
69     'symbian=s' => \$SymbianVersion,
70     'perl=s'    => \$PerlVersion,
71     'csuffix=s' => \$CSuffix,
72     'cplusplus' => \$CPlusPlus,
73     'win=s'     => \$WIN,
74     'arm=s'     => \$ARM,
75     'config'    => \$Config,
76     'build'     => \$Build,
77     'clean'     => \$Clean,
78     'distclean' => \$DistClean,
79     'sis'       => \$Sis
80   );
81
82 usage() unless @ARGV;
83
84 $CSuffix = '.cpp' if $CPlusPlus;
85 $Build = !( $Config || $Clean || $DistClean ) || $Sis unless defined $Build;
86
87 die "$0: Symbian version undefined\n" unless defined $SymbianVersion;
88
89 $SymbianVersion =~ s:/:\\:g;
90
91 die "$0: Symbian version '$SymbianVersion' not found\n"
92   unless -d "\\Symbian\\$SymbianVersion";
93
94 die "$0: Perl version undefined\n" unless defined $PerlVersion;
95
96 die "$0: Perl version '$PerlVersion' not found\n"
97   if !$CoreBuild && !-d "\\Symbian\\Perl\\$PerlVersion";
98
99 print "Configuring with Symbian $SymbianVersion and Perl $PerlVersion...\n";
100
101 $PERLSDK = "\\Symbian\\Perl\\$PerlVersion";
102
103 $R_V_SV = $PerlVersion;
104
105 $VERSION = $PerlVersion unless defined $VERSION;
106
107 $VERSION =~ tr/.//d if defined $VERSION;
108
109 $ENV{SDK}   = $SYMBIAN_ROOT;    # For the Errno extension
110 $ENV{CROSS} = 1;                # For the Encode extension (unbuilt now)
111
112 my $UARM = 'urel';
113 my $UREL = "$SYMBIAN_ROOT\\epoc32\\release\\-ARM-\\$UARM";
114 my $SRCDBG;
115 if (exists $ENV{UREL}) {
116     $UREL = $ENV{UREL}; # from sdk.pl
117     $UREL =~ s/-ARM-/$ARM/;
118     $UARM = $ENV{UARM}; # from sdk.pl
119     $SRCDBG = $UARM eq 'udeb' ? "SRCDBG" : "";
120 }
121
122 my %CONF;
123 my %EXTCFG;
124
125 sub write_bld_inf {
126     my ($base) = @_;
127     print "\tbld.inf\n";
128     open( BLD_INF, ">bld.inf" ) or die "$0: bld.inf: $!\n";
129     print BLD_INF <<__EOF__;
130 PRJ_MMPFILES
131 $base.mmp
132 PRJ_PLATFORMS
133 $WIN $ARM
134 __EOF__
135     close(BLD_INF);
136 }
137
138 sub system_echo {
139     my $cmd = shift;
140     print "xsbuild: ", $cmd, "\n";
141     return system($cmd);
142 }
143
144 sub run_PL {
145     my ( $PL, $dir, $file ) = @_;
146     if ( defined $file ) {
147         print "\t(Running $dir\\$PL to create $file)\n";
148         unlink($file);
149     }
150     else {
151         print "\t(Running $dir\\$PL)\n";
152     }
153     my $cmd;
154     if ($CoreBuild) {
155         # Problem: the Config.pm we have in $BUILDROOT\\lib carries the
156         # version number of the Perl we are building, while the Perl
157         # we are running might have some other version.  Solution:
158         # temporarily replace the Config.pm with a patched version.
159         my $V = sprintf "%vd", $^V;
160         unlink("$BUILDROOT\\lib\\Config.pm.bak");
161         print "(patching $BUILDROOT\\lib\\Config.pm)\n";
162         system_echo("perl -pi.bak -e \"s:\\Q$R_V_SV:$V:\" $BUILDROOT\\lib\\Config.pm");
163     }
164     system_echo("perl -I$BUILDROOT\\lib -I$BUILDROOT\\xlib\\symbian -I$BUILDROOT\\t\\lib $PL") == 0
165       or warn "$0: $PL failed.\n";
166     if ($CoreBuild) {
167         system_echo("copy $BUILDROOT\\lib\\Config.pm.bak $BUILDROOT\\lib\\Config.pm");
168     }
169     if ( defined $file ) { -s $file or die "$0: No $file created.\n" }
170 }
171
172 sub read_old_multi {
173     my ( $conf, $k ) = @_;
174     push @{ $conf->{$k} }, split( ' ', $1 ) if /^$k\s(.+)$/;
175 }
176
177 sub uniquefy_filenames {
178     my $b = [];
179     my %c = ();
180     for my $i (@{$_[0]}) {
181         $i =~ s!/!\\!g;
182         $i = lc $i if $i =~ m!\\!;
183         $i =~ s!^c:!!;
184         push @$b, $i unless $c{$i}++;
185     }
186     return $b;
187 }
188
189 sub read_mmp {
190     my ( $conf, $mmp ) = @_;
191     if ( -r $mmp && open( MMP, "<$mmp" ) ) {
192         print "\tReading $mmp...\n";
193         while (<MMP>) {
194             chomp;
195             $conf->{TARGET}     = $1 if /^TARGET\s+(.+)$/;
196             $conf->{TARGETPATH} = $1 if /^TARGETPATH\s+(.+)$/;
197             $conf->{EXTVERSION} = $1 if /^EXTVERSION\s+(.+)$/;
198             read_old_multi( $conf, "SOURCE" );
199             read_old_multi( $conf, "SOURCEPATH" );
200             read_old_multi( $conf, "USERINCLUDE" );
201             read_old_multi( $conf, "SYSTEMINCLUDE" );
202             read_old_multi( $conf, "LIBRARY" );
203             read_old_multi( $conf, "MACRO" );
204         }
205         close(MMP);
206     }
207 }
208
209 sub write_mmp {
210     my ( $ext, $base, $userinclude, @src ) = @_;
211
212     my $extdash = $ext; $extdash =~ s!\\!-!g;
213
214     print "\t$base.mmp\n";
215     $CONF{TARGET}        = "perl$VERSION-$extdash.dll";
216     $CONF{TARGETPATH}    = "\\System\\Libs\\Perl\\$R_V_SV";
217     $CONF{SOURCE}        = [@src];
218     $CONF{SOURCEPATH}    = [ $CWD, $BUILDROOT ];
219     $CONF{USERINCLUDE}   = [ $CWD, $BUILDROOT ];
220     $CONF{SYSTEMINCLUDE} = ["$PERLSDK\\include"] unless $CoreBuild;
221     $CONF{SYSTEMINCLUDE} = [ $BUILDROOT ] if $CoreBuild;
222     $CONF{LIBRARY}       = [];
223     $CONF{MACRO}         = [];
224     read_mmp( \%CONF, "_init.mmp" );
225     read_mmp( \%CONF, "$base.mmp" );
226
227     if ($base eq 'Zlib') {
228         push @{$CONF{USERINCLUDE}}, "$CWD\\zlib-src";
229     }
230
231     for my $ui ( @{$userinclude} ) {
232         $ui =~ s!/!\\!g;
233         if ( $ui =~ m!^(?:[CD]:)?\\! ) {
234             push @{ $CONF{USERINCLUDE} }, $ui;
235         }
236         else {
237             push @{ $CONF{USERINCLUDE} }, "$BUILDROOT\\$ui";
238         }
239     }
240     push @{ $CONF{SYSTEMINCLUDE} }, "\\epoc32\\include";
241     push @{ $CONF{SYSTEMINCLUDE} }, "\\epoc32\\include\\libc";
242     push @{ $CONF{LIBRARY} },       "euser.lib";
243     push @{ $CONF{LIBRARY} },       "estlib.lib";
244     push @{ $CONF{LIBRARY} },       "perl$VERSION.lib";
245     push @{ $CONF{MACRO} },         "SYMBIAN" unless $CoreBuild;
246     push @{ $CONF{MACRO} },         "PERL_EXT" if $CoreBuild;
247     push @{ $CONF{MACRO} },         "MULTIPLICITY";
248     push @{ $CONF{MACRO} },         "PERL_IMPLICIT_CONTEXT";
249     push @{ $CONF{MACRO} },         "PERL_GLOBAL_STRUCT";
250     push @{ $CONF{MACRO} },         "PERL_GLOBAL_STRUCT_PRIVATE";
251
252     for my $u (qw(SOURCE SOURCEPATH SYSTEMINCLUDE USERINCLUDE LIBRARY MACRO)) {
253         $CONF{$u} = uniquefy_filenames( $CONF{$u} );
254     }
255     open( BASE_MMP, ">$base.mmp" ) or die "$0: $base.mmp: $!\n";
256
257     print BASE_MMP <<__EOF__;
258 TARGET          $CONF{TARGET}
259 TARGETTYPE      dll
260 TARGETPATH      $CONF{TARGETPATH}
261 SOURCE          @{$CONF{SOURCE}}
262 $SRCDBG
263 __EOF__
264     for my $u (qw(SOURCEPATH SYSTEMINCLUDE USERINCLUDE)) {
265         for my $v ( @{ $CONF{$u} } ) {
266             print BASE_MMP "$u\t$v\n";
267         }
268     }
269     # OPTION does not work in MMPs for pre-2.0 SDKs?
270     print BASE_MMP <<__EOF__;
271 LIBRARY         @{$CONF{LIBRARY}}
272 MACRO           @{$CONF{MACRO}}
273 // OPTION       MSVC /P
274 // OPTION       GCC -E
275 __EOF__
276     close(BASE_MMP);
277
278 }
279
280 sub write_makefile {
281     my ( $base, $build ) = @_;
282
283     print "\tMakefile\n";
284
285     my $windef1 = "$SYMBIAN_ROOT\\Epoc32\\Build$CWD\\$base\\$WIN\\$base.def";
286     my $windef2 = "..\\BWINS\\${base}u.def";
287     my $armdef1 = "$SYMBIAN_ROOT\\Epoc32\\Build$CWD\\$base\\$ARM\\$base.def";
288     my $armdef2 = "..\\BMARM\\${base}u.def";
289
290     my $wrap = $SYMBIAN_ROOT && $S60SDK eq '1.2' && $SYMBIAN_ROOT !~ /_CW$/;
291     my $ABLD = $wrap ? 'perl b.pl' : 'abld';
292
293     open( MAKEFILE, ">Makefile" ) or die "$0: Makefile: $!\n";
294     print MAKEFILE <<__EOF__;
295 WIN = $WIN
296 ARM = $ARM
297 ABLD = $ABLD
298
299 all:    build freeze
300
301 sis:    build_arm freeze_arm
302
303 build:  abld.bat build_win build_arm
304
305 abld.bat:
306         bldmake bldfiles
307
308 build_win: abld.bat
309         bldmake bldfiles
310         \$(ABLD) build \$(WIN) udeb
311
312 build_arm: abld.bat
313         bldmake bldfiles
314         \$(ABLD) build \$(ARM) $UARM
315
316 win:    build_win freeze_win
317
318 arm:    build_arm freeze_arm
319
320 freeze: freeze_win freeze_arm
321
322 freeze_win:
323         bldmake bldfiles
324         \$(ABLD) freeze \$(WIN) $base
325
326 freeze_arm:
327         bldmake bldfiles
328         \$(ABLD) freeze \$(ARM) $base
329
330 defrost:        defrost_win defrost_arm
331
332 defrost_win:
333         -del /f $windef1
334         -del /f $windef2
335
336 defrost_arm:
337         -del /f $armdef1
338         -del /f $armdef2
339
340 clean:  clean_win clean_arm
341
342 clean_win:
343         \$(ABLD) clean \$(WIN)
344
345 clean_arm:
346         \$(ABLD) clean \$(ARM)
347
348 realclean:      clean realclean_win realclean_arm
349         -del /f _init.c b.pl
350         -del /f $base.c $base.mmp
351
352 realclean_win:
353         \$(ABLD) reallyclean \$(WIN)
354
355 realclean_arm:
356         \$(ABLD) reallyclean \$(ARM)
357
358 distclean:      defrost realclean
359         -rmdir ..\\BWINS ..\\BMARM
360         -del /f const-c.inc const-xs.inc
361         -del /f Makefile abld.bat bld.inf
362 __EOF__
363     close(MAKEFILE);
364     if ($wrap) {
365         if(open(B,">b.pl")) {
366             print B <<'__EOF__';
367 # abld.pl wrapper.
368
369 # nmake doesn't like MFLAGS and MAKEFLAGS being set to -w and w.
370 delete $ENV{MFLAGS};
371 delete $ENV{MAKEFLAGS};
372
373 print "abld @ARGV\n";
374 system_echo("abld @ARGV");
375 __EOF__
376             close(B);
377         } else {
378             warn "$0: failed to create b.pl: $!\n";
379         }
380     }
381 }
382
383 sub update_dir {
384     print "[chdir from ", getcwd(), " to ";
385     chdir(shift) or return;
386     update_cwd();
387     print getcwd(), "]\n";
388 }
389
390 sub xsconfig {
391     my ( $ext, $dir ) = @_;
392     print "Configuring for $ext, directory '$dir'...\n";
393     my $extu = $CoreBuild ? "$BUILDROOT\\lib\\ExtUtils" : "$PERLSDK\\lib\\ExtUtils";
394     update_dir($dir) or die "$0: chdir '$dir': $!\n";
395     my $build  = dirname($ext);
396     my $base   = basename($ext);
397     my $basexs = "$base.xs";
398     my $basepm = "$base.pm";
399     my $basec  = "$base$CSuffix";
400     my $extdir = ".";
401     if ( $dir =~ m:^ext\\(.+): ) {
402         $extdir = $1;
403     }
404     elsif ( $dir ne "." ) {
405         $extdir = $dir;
406     }
407     my $extdirdir  = dirname($extdir);
408     my $targetroot = "\\System\\Libs\\Perl\\$R_V_SV";
409     write_bld_inf($base) if -f $basexs;
410
411     my %src;
412     $src{$basec}++;
413
414     $extdirdir = $extdirdir eq "." ? "" : "$extdirdir\\";
415
416     my $extdash = $ext; $extdash =~ s!\\!-!g;
417
418     my %lst;
419     $lst{"$UREL\\perl$VERSION-$extdash.dll"} =
420       "$targetroot\\$ARM-symbian\\$base.dll"
421       if -f $basexs;
422     $lst{"$dir\\$base.pm"} = "$targetroot\\$extdirdir$base.pm"
423       if -f $basepm && $base ne 'XSLoader';
424
425     my %incdir;
426     my $ran_PL;
427     if ( -d 'lib' ) {
428         use File::Find;
429         my @found;
430         find( sub { push @found, $File::Find::name if -f $_ }, 'lib' );
431         for my $found (@found) {
432             next if $found =~ /\.bak$/i; # Zlib
433             my ($short) = ( $found =~ m/^lib.(.+)/ );
434             $short =~ s!/!\\!g;
435             $found =~ s!/!\\!g;
436             $lst{"$dir\\$found"} = "$targetroot\\$short";
437         }
438     }
439     if ( my @pm = glob("*.pm */*.pm") ) {
440         for my $pm (@pm) {
441             next if $pm =~ m:^t/:;
442             $pm =~ s:/:\\:g;
443             $lst{"$dir\\$pm"} = "$targetroot\\$extdirdir$pm";
444         }
445     }
446     if ( my @c = glob("*.c *.cpp */*.c */*.cpp") ) {
447         @c = grep { ! m:^zlib-src/: } @c if $ext eq 'ext\Compress\Zlib';
448         for my $c (@c) {
449             $c =~ s:/:\\:g;
450             $src{$c}++;
451         }
452     }
453     if ( my @h = glob("*.h */*.h") ) {
454         @h = grep { ! m:^zlib-src/: } @h if $ext eq 'ext\Compress\Zlib';
455         for my $h (@h) {
456             $h =~ s:/:\\:g;
457             $h = dirname($h);
458             $incdir{"$dir\\$h"}++ unless $h eq ".";
459         }
460     }
461     if ( exists $EXTCFG{$ext} ) {
462         for my $cfg ( @{ $EXTCFG{$ext} } ) {
463             if ( $cfg =~ /^([-+])?(.+\.(c|cpp|h))$/ ) {
464                 my $o = defined $1 ? $1 : '+';
465                 my $f = $2;
466                 $f =~ s:/:\\:g;
467                 for my $f ( glob($f) ) {
468                     if ( $o eq '+' ) {
469                         warn "$0: no source file $dir\\$f\n" unless -f $f;
470                         $src{$f}++ unless $cfg =~ /\.h$/;
471                         if ( $f =~ m:^(.+)\\[^\\]+$: ) {
472                             $incdir{$1}++;
473                         }
474                     }
475                     elsif ( $o eq '-' ) {
476                         delete $src{$f};
477                     }
478                 }
479             }
480             if ( $cfg =~ /^([-+])?(.+\.(pm|pl|inc))$/ ) {
481                 my $o = defined $1 ? $1 : '+';
482                 my $f = $2;
483                 $f =~ s:/:\\:g;
484                 for my $f ( glob($f) ) {
485                     if ( $o eq '+' ) {
486                         warn "$0: no Perl file $dir\\$f\n" unless -f $f;
487                         $lst{"$dir\\$f"} = "$targetroot\\$extdir\\$f";
488                     }
489                     elsif ( $o eq '-' ) {
490                         delete $lst{"$dir\\$f"};
491                     }
492                 }
493             }
494             if ( $cfg eq 'CONST' && !$ran_PL++ ) {
495                 run_PL( "Makefile.PL", $dir, "const-xs.inc" );
496             }
497         }
498     }
499     unless ( $ran_PL++ ) {
500         run_PL( "Makefile.PL", $dir ) if -f "Makefile.PL";
501     }
502     if ( $dir eq "ext\\Errno" ) {
503         run_PL( "Errno_pm.PL", $dir, "Errno.pm" );
504         $lst{"$dir\\Errno.pm"} = "$targetroot\\Errno.pm";
505     }
506     elsif ( $dir eq "ext\\Devel\\PPPort" ) {
507         run_PL( "ppport_h.PL", $dir, "ppport.h" );
508     }
509     elsif ( $dir eq "ext\\DynaLoader" ) {
510         run_PL( "XSLoader_pm.PL", $dir, "XSLoader.pm" );
511         $lst{"ext\\DynaLoader\\XSLoader.pm"} = "$targetroot\\XSLoader.pm";
512     }
513     elsif ( $dir eq "ext\\Encode" ) {
514         system_echo("perl bin\\enc2xs -Q -O -o def_t.c -f def_t.fnm") == 0
515           or die "$0: running enc2xs failed: $!\n";
516     }
517
518     my @lst = sort keys %lst;
519
520     read_mmp( \%CONF, "_init.mmp" );
521     read_mmp( \%CONF, "$base.mmp" );
522
523     if ( -f $basexs ) {
524         my %MM;    # MakeMaker results
525         my @MM = qw(VERSION XS_VERSION);
526         if ( -f "Makefile" ) {
527             print "\tReading MakeMaker Makefile...\n";
528             if ( open( MAKEFILE, "Makefile" ) ) {
529                 while (<MAKEFILE>) {
530                     for my $m (@MM) {
531                         if (m!^$m = (.+)!) {
532                             $MM{$m} = $1;
533                             print "\t$m = $1\n";
534                         }
535                     }
536                 }
537                 close(MAKEFILE);
538             }
539             else {
540                 warn "$0: Makefile: $!";
541             }
542             print "\tDeleting MakeMaker Makefile.\n";
543             unlink("Makefile");
544         }
545
546         unlink($basec);
547         print "\t$basec\n";
548         if ( defined $CONF{EXTVERSION} ) {
549             my $EXTVERSION = $CONF{EXTVERSION};
550             print "\tUsing $EXTVERSION for version...\n";
551             $MM{VERSION} = $MM{XS_VERSION} = $EXTVERSION;
552         }
553         die "VERSION or XS_VERSION undefined\n"
554           unless defined $MM{VERSION} && defined $MM{XS_VERSION};
555         if ( open( BASE_C, ">$basec" ) ) {
556             print BASE_C <<__EOF__;
557 #ifndef VERSION
558 #define VERSION "$MM{VERSION}"
559 #endif
560 #ifndef XS_VERSION
561 #define XS_VERSION "$MM{XS_VERSION}"
562 #endif
563 __EOF__
564             close(BASE_C);
565         }
566         else {
567             warn "$0: $basec: $!";
568         }
569         unless (
570             system_echo(
571 "perl -I$BUILDROOT\\lib -I$PERLSDK\\lib $extu\\xsubpp -csuffix .cpp -typemap $extu\\typemap -noprototypes $basexs >> $basec"
572             ) == 0
573             && -s $basec
574           )
575         {
576             die "$0: perl xsubpp failed: $!\n";
577         }
578
579         print "\t_init.c\n";
580         open( _INIT_C, ">_init.c" ) or die "$!: _init.c: $!\n";
581         print _INIT_C <<__EOF__;
582     #include "EXTERN.h"
583     #include "perl.h"
584     EXPORT_C void _init(void *handle) {
585     }
586 __EOF__
587         close(_INIT_C);
588
589         my @src = ( "_init.c", sort keys %src );
590
591         if ( $base eq "Encode" ) {    # Currently unused.
592             for my $submf ( glob("*/Makefile") ) {
593                 my $d = dirname($submf);
594                 print "Configuring Encode::$d...\n";
595                 if ( open( SUBMF, $submf ) ) {
596                     if ( update_dir($d) ) {
597                         my @subsrc;
598                         while (<SUBMF>) {
599                             next if 1 .. /postamble/;
600                             if (m!^(\w+_t)\.c : !) {
601                                 system_echo(
602                                     "perl ..\\bin\\enc2xs -Q -o $1.c -f $1.fnm")
603                                   == 0
604                                   or warn "$0: enc2xs: $!\n";
605                                 push @subsrc, "$1.c";
606                             }
607                         }
608                         close(SUBMF);
609                         unlink($submf);
610                         my $subbase = $d;
611                         $subbase =~ s!/!::!g;
612                         write_mmp( $ext, $subbase, ["..\\Encode"], "$subbase.c",
613                             @subsrc );
614                         write_makefile( $subbase, $build );
615                         write_bld_inf($subbase);
616
617                         unless (
618                             system_echo(
619 "perl -I$BUILDROOT\\lib ..\\$extu\\xsubpp -csuffix .cpp -typemap ..\\$extu\\typemap -noprototypes $subbase.xs > $subbase.c"
620                             ) == 0
621                             && -s "$subbase.c"
622                           )
623                         {
624                             die "$0: perl xsubpp failed: $!\n";
625                         }
626                         update_dir("..");
627                     }
628                     else {
629                         warn "$0: chdir $d: $!\n";
630                     }
631                 }
632                 else {
633                     warn "$0: $submf: $!";
634                 }
635             }
636             print "Configuring Encode...\n";
637         }
638
639         write_mmp( $ext, $base, [ keys %incdir ], @src );
640         write_makefile( $base, $build );
641     }
642     my $lstname = $ext;
643     $lstname =~ s:^ext\\::;
644     $lstname =~ s:\\:-:g;
645     print "\t$lstname.lst\n";
646     my $lstout =
647       $CoreBuild ? "$BUILDROOT/symbian/$lstname.lst" : "$BUILDROOT/$lstname.lst";
648     if ( open( my $lst, ">$lstout" ) ) {
649         for my $f (@lst) { print $lst qq["$f"-"!:$lst{$f}"\n] }
650         close($lst);
651     }
652     else {
653         die "$0: $lstout: $!\n";
654     }
655     update_dir($BUILDROOT);
656 }
657
658 sub update_cwd {
659     $CWD = getcwd();
660     $CWD =~ s!^[CD]:!!i;
661     $CWD =~ s!/!\\!g;
662 }
663
664 for my $ext (@ARGV) {
665
666     $ext =~ s!::!\\!g;
667     my $extdash = "ext\\$ext"; $extdash =~ s!\\!-!g;
668     $ext =~ s!/!\\!g;
669
670     my $cfg;
671
672     $cfg = $2 if $ext =~ s/(.+?),(.+)/$1/;
673
674     my $dir;
675
676     unless ( -e $ext ) {
677         if ( $ext =~ /\.xs$/ && !-f $ext ) {
678             if ( -f "ext\\$ext" ) {
679                 $ext = "ext\\$ext";
680                 $dir = dirname($ext);
681             }
682         }
683         elsif ( !-d $ext ) {
684             if ( -d "ext\\$ext" ) {
685                 $ext = "ext\\$ext";
686                 $dir = $ext;
687             }
688         }
689         $dir = "." unless defined $dir;
690     }
691     else {
692         if ( $ext =~ /\.xs$/ && -f $ext ) {
693             $ext = dirname($ext);
694             $dir = $ext;
695         }
696         elsif ( -d $ext ) {
697             $dir = $ext;
698         }
699     }
700
701     if ( $ext eq "XSLoader" ) {
702         $ext = "ext\\XSLoader";
703     }
704     if ( $ext eq "ext\\XSLoader" ) {
705         $dir = "ext\\DynaLoader";
706     }
707
708     $EXTCFG{$ext} = [ split( /,/, $cfg ) ] if defined $cfg;
709
710     die "$0: no lib\\Config.pm\n"
711       if $CoreBuild && $Build && !-f "lib\\Config.pm";
712
713     if ($CoreBuild) {
714         open( my $cfg, "symbian/install.cfg" )
715           or die "$0: symbian/install.cfg: $!\n";
716         my $extdir = $dir;
717         $extdir =~ s:^ext\\::;
718         while (<$cfg>) {
719             next unless /^ext\s+(.+)/;
720             chomp;
721             my $ext = $1;
722             my @ext = split( ' ', $ext );
723             $EXTCFG{"ext\\$ext[0]"} = [@ext];
724         }
725         close($cfg);
726     }
727
728     if ( $Config || $Build ) {
729         xsconfig( $ext, $dir ) or die "$0: xsconfig '$ext' failed\n";
730         next if $Config;
731     }
732
733     if ($dir eq ".") {
734         warn "$0: No directory for $ext, skipping...\n";
735         next;
736     }
737
738     my $chdir = $ext eq "ext\\XSLoader" ? "ext\\DynaLoader" : $dir;
739     die "$0: no directory '$chdir'\n" unless -d $chdir;
740     update_dir($chdir) or die "$0: chdir '$chdir' failed: $!\n";
741
742     my %CONF;
743
744     my @ext   = split( /\\/, $ext );
745     my $base  = $ext[-1];
746
747     if ( $Clean || $DistClean ) {
748         print "Cleaning $ext...\n";
749         unlink("bld.inf");
750         unlink("$base.mmp");
751         unlink("_init.c");
752         unlink("const-c.inc");
753         unlink("const-xs.inc");
754         rmdir("..\\bmarm");
755     }
756
757     if ( $Build && $ext ne "ext\\XSLoader" && $ext ne "ext\\Errno" ) {
758
759      # We compile the extension three (3) times.
760      # (1) Only the _init.c to get _init() as the ordinal 1 function in the DLL.
761      # (2) With the rest and the _init.c to get ordinals for the rest.
762      # (3) With an updated _init.c that carries the symbols from step (2).
763
764         system_echo("make clean");
765         system_echo("make defrost") == 0 or warn "$0: make defrost failed\n";
766
767         my @TARGET;
768
769         push @TARGET, 'sis' if $Sis;
770
771         # Compile #1.
772         # Hide all but the _init.c.
773         print "\n*** $ext - Compile 1 of 3.\n\n";
774         print "(patching $base.mmp)\n";
775         system(
776 "perl -pi.bak -e \"s:^SOURCE\\s+_init.c:SOURCE\\t_init.c // :\" $base.mmp"
777         );
778         system_echo("bldmake bldfiles");
779         system_echo("make @TARGET") == 0 or die "$0: make #1 failed\n";
780
781         # Compile #2.
782         # Reveal the rest again.
783         print "\n*** $ext - Compile 2 of 3.\n\n";
784         print "(patching $base.mmp)\n";
785         system(
786 "perl -pi.bak -e \"s:^SOURCE\\t_init.c // :SOURCE\\t_init.c :\" $base.mmp"
787         );
788         system_echo("make @TARGET") == 0 or die "$0: make #2 failed\n";
789         unlink("$base.mmp.bak");
790
791         open( _INIT_C, ">_init.c" ) or die "$0: _init.c: $!\n";
792         print _INIT_C <<'__EOF__';
793 #include "EXTERN.h"
794 #include "perl.h"
795
796 /* This is a different but matching definition from in dl_symbian.xs. */
797 typedef struct {
798     void*       handle;
799     int         error;
800     HV*         symbols;
801 } PerlSymbianLibHandle;
802
803 EXPORT_C void _init(void* handle) {
804 __EOF__
805
806         my %symbol;
807         my $def;
808         my $basef;
809         for my $f ("$SYMBIAN_ROOT\\Epoc32\\Build$CWD\\$base\\WINS\\perl$VERSION-$extdash.def",
810                    "..\\BMARM\\perl$VERSION-${extdash}u.def") {
811             print "\t($f - ";
812             if ( open( $def, $f ) ) {
813                 print "OK)\n";
814                 $basef = $f;
815                 last;
816             } else {
817                 print "no)\n";
818             }
819         }
820         unless (defined $basef) {
821             die "$0: failed to find .def for $base\n";
822         }
823         while (<$def>) {
824             next while 1 .. /^EXPORTS/;
825             if (/^\s*(\w+) \@ (\d+) /) {
826                 $symbol{$1} = $2;
827             }
828         }
829         close($def);
830  
831         my @symbol = sort keys %symbol;
832         if (@symbol) {
833             print _INIT_C <<'__EOF__';
834     dTHX;
835     PerlSymbianLibHandle* h = (PerlSymbianLibHandle*)handle;
836     if (!h->symbols)
837         h->symbols = newHV();
838     if (h->symbols) {
839 __EOF__
840             for my $sym (@symbol) {
841                 my $len = length($sym);
842                 print _INIT_C <<__EOF__;
843         hv_store(h->symbols, "$sym", $len, newSViv($symbol{$sym}), 0);
844 __EOF__
845             }
846         }
847         else {
848             die "$0: $basef: no exports found\n";
849         }
850
851         print _INIT_C <<'__EOF__';
852     }
853 }
854 __EOF__
855         close(_INIT_C);
856
857         # Compile #3.  This is for real.
858         print "\n*** $ext - Compile 3 of 3.\n\n";
859         system_echo("make @TARGET") == 0 or die "$0: make #3 failed\n";
860
861     }
862     elsif ( $Clean || $DistClean ) {
863         if ( $ext eq "ext\\Errno" ) {
864             unlink( "Errno.pm", "Makefile" );
865         }
866         else {
867             if ( -f "Makefile" ) {
868                 if ($Clean) {
869                     system_echo("make clean") == 0 or die "$0: make clean failed\n";
870                 }
871                 elsif ($DistClean) {
872                     system_echo("make distclean") == 0
873                       or die "$0: make distclean failed\n";
874                 }
875             }
876             if ( $ext eq "ext\\Compress\\Zlib" ) {
877                 my @bak;
878                 find( sub { push @bak, $File::Find::name if /\.bak$/ }, "." );
879                 unlink(@bak) if @bak;
880                 my @src;
881                 find( sub { push @src, $_ if -f $_ }, "zlib-src" );
882                 unlink(@src) if @src;
883                 unlink("constants.xs");
884             }
885             if ( $ext eq "ext\\Devel\\PPPort" ) {
886                 unlink("ppport.h");
887             }
888         }
889         my @D = glob("../BMARM/*.def ../BWINS/*.def");
890         unlink(@D) if @D;
891         my @B = glob("ext/BWINS ext/BMARM ext/*/BWINS ext/*/BMARM Makefile");
892         rmdir(@B) if @B;
893     }
894
895     update_dir($BUILDROOT);
896
897 }    # for my $ext
898
899 exit(0);
900