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