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