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