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