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