1#!./miniperl 2use strict; 3use warnings; 4use Config; 5BEGIN { 6 if ($^O eq 'MSWin32') { 7 unshift @INC, ('../cpan/Cwd', '../cpan/Cwd/lib'); 8 require File::Spec::Functions; 9 require FindExt; 10 } 11 else { 12 unshift @INC, 'cpan/Cwd'; 13 } 14} 15use Cwd; 16 17my $is_Win32 = $^O eq 'MSWin32'; 18my $is_VMS = $^O eq 'VMS'; 19my $is_Unix = !$is_Win32 && !$is_VMS; 20 21# To clarify, this isn't the entire suite of modules considered "toolchain" 22# It's not even all modules needed to build ext/ 23# It's just the source paths of the (minimum complete set of) modules in ext/ 24# needed to build the nonxs modules 25# After which, all nonxs modules are in lib, which was always sufficient to 26# allow miniperl to build everything else. 27 28# This list cannot get any longer without overflowing the length limit for 29# environment variables on VMS 30my @toolchain = qw(cpan/AutoLoader/lib 31 cpan/Cwd cpan/Cwd/lib 32 cpan/ExtUtils-Command/lib 33 dist/ExtUtils-Install/lib 34 cpan/ExtUtils-MakeMaker/lib 35 cpan/ExtUtils-Manifest/lib 36 cpan/File-Path/lib 37 ); 38 39# Used only in ExtUtils::Liblist::Kid::_win32_ext() 40push @toolchain, 'cpan/Text-ParseWords/lib' if $is_Win32; 41 42my @ext_dirs = qw(cpan dist ext); 43my $ext_dirs_re = '(?:' . join('|', @ext_dirs) . ')'; 44 45# This script acts as a simple interface for building extensions. 46 47# It's actually a cut and shut of the Unix version ext/utils/makeext and the 48# Windows version win32/build_ext.pl hence the two invocation styles. 49 50# On Unix, it primarily used by the perl Makefile one extention at a time: 51# 52# d_dummy $(dynamic_ext): miniperl preplibrary FORCE 53# @$(RUN) ./miniperl make_ext.pl --target=dynamic $@ MAKE=$(MAKE) LIBPERL_A=$(LIBPERL) 54# 55# On Windows or VMS, 56# If '--static' is specified, static extensions will be built. 57# If '--dynamic' is specified, dynamic extensions will be built. 58# If '--nonxs' is specified, nonxs extensions will be built. 59# If '--dynaloader' is specificied, DynaLoader will be built. 60# If '--all' is specified, all extensions will be built. 61# 62# make_ext.pl "MAKE=make [-make_opts]" --dir=directory [--target=target] [--static|--dynamic|--all] +ext2 !ext1 63# 64# E.g. 65# 66# make_ext.pl "MAKE=nmake -nologo" --dir=..\ext 67# 68# make_ext.pl "MAKE=nmake -nologo" --dir=..\ext --target=clean 69# 70# make_ext.pl MAKE=dmake --dir=..\ext 71# 72# make_ext.pl MAKE=dmake --dir=..\ext --target=clean 73# 74# Will skip building extensions which are marked with an '!' char. 75# Mostly because they still not ported to specified platform. 76# 77# If any extensions are listed with a '+' char then only those 78# extensions will be built, but only if they arent countermanded 79# by an '!ext' and are appropriate to the type of building being done. 80 81# It may be deleted in a later release of perl so try to 82# avoid using it for other purposes. 83 84my (%excl, %incl, %opts, @extspec, @pass_through); 85 86foreach (@ARGV) { 87 if (/^!(.*)$/) { 88 $excl{$1} = 1; 89 } elsif (/^\+(.*)$/) { 90 $incl{$1} = 1; 91 } elsif (/^--([\w\-]+)$/) { 92 $opts{$1} = 1; 93 } elsif (/^--([\w\-]+)=(.*)$/) { 94 push @{$opts{$1}}, $2; 95 } elsif (/=/) { 96 push @pass_through, $_; 97 } elsif (length) { 98 push @extspec, $_; 99 } 100} 101 102my $static = $opts{static} || $opts{all}; 103my $dynamic = $opts{dynamic} || $opts{all}; 104my $nonxs = $opts{nonxs} || $opts{all}; 105my $dynaloader = $opts{dynaloader} || $opts{all}; 106 107# The Perl Makefile.SH will expand all extensions to 108# lib/auto/X/X.a (or lib/auto/X/Y/Y.a if nested) 109# A user wishing to run make_ext might use 110# X (or X/Y or X::Y if nested) 111 112# canonise into X/Y form (pname) 113 114foreach (@extspec) { 115 if (s{^lib/auto/}{}) { 116 # Remove lib/auto prefix and /*.* suffix 117 s{/[^/]+\.[^/]+$}{}; 118 } elsif (s{^$ext_dirs_re/}{}) { 119 # Remove ext/ prefix and /pm_to_blib suffix 120 s{/pm_to_blib$}{}; 121 # Targets are given as files on disk, but the extension spec is still 122 # written using /s for each :: 123 tr!-!/!; 124 } elsif (s{::}{\/}g) { 125 # Convert :: to / 126 } else { 127 s/\..*o//; 128 } 129} 130 131my $makecmd = shift @pass_through; # Should be something like MAKE=make 132unshift @pass_through, 'PERL_CORE=1'; 133 134my @dirs = @{$opts{dir} || \@ext_dirs}; 135my $target = $opts{target}[0]; 136$target = 'all' unless defined $target; 137 138# Previously, $make was taken from config.sh. However, the user might 139# instead be running a possibly incompatible make. This might happen if 140# the user types "gmake" instead of a plain "make", for example. The 141# correct current value of MAKE will come through from the main perl 142# makefile as MAKE=/whatever/make in $makecmd. We'll be cautious in 143# case third party users of this script (are there any?) don't have the 144# MAKE=$(MAKE) argument, which was added after 5.004_03. 145unless(defined $makecmd and $makecmd =~ /^MAKE=(.*)$/) { 146 die "$0: WARNING: Please include MAKE=\$(MAKE) in \@ARGV\n"; 147} 148 149# This isn't going to cope with anything fancy, such as spaces inside command 150# names, but neither did what it replaced. Once there is a use case that needs 151# it, please supply patches. Until then, I'm sticking to KISS 152my @make = split ' ', $1 || $Config{make} || $ENV{MAKE}; 153# Using an array of 0 or 1 elements makes the subsequent code simpler. 154my @run = $Config{run}; 155@run = () if not defined $run[0] or $run[0] eq ''; 156 157 158if ($target eq '') { 159 die "make_ext: no make target specified (eg all or clean)\n"; 160} elsif ($target !~ /(?:^all|clean)$/) { 161 # for the time being we are strict about what make_ext is used for 162 die "$0: unknown make target '$target'\n"; 163} 164 165if (!@extspec and !$static and !$dynamic and !$nonxs and !$dynaloader) { 166 die "$0: no extension specified\n"; 167} 168 169my $perl; 170my %extra_passthrough; 171 172if ($is_Win32) { 173 (my $here = getcwd()) =~ s{/}{\\}g; 174 $perl = $^X; 175 if ($perl =~ m#^\.\.#) { 176 $perl = "$here\\$perl"; 177 } 178 (my $topdir = $perl) =~ s/\\[^\\]+$//; 179 # miniperl needs to find perlglob and pl2bat 180 $ENV{PATH} = "$topdir;$topdir\\win32\\bin;$ENV{PATH}"; 181 my $pl2bat = "$topdir\\win32\\bin\\pl2bat"; 182 unless (-f "$pl2bat.bat") { 183 my @args = ($perl, "-I$topdir\\lib", ("$pl2bat.pl") x 2); 184 print "@args\n"; 185 system(@args) unless defined $::Cross::platform; 186 } 187 188 my $build = getcwd(); 189 print "In $build"; 190 foreach my $dir (@dirs) { 191 chdir($dir) or die "Cannot cd to $dir: $!\n"; 192 (my $ext = getcwd()) =~ s{/}{\\}g; 193 FindExt::scan_ext($ext); 194 FindExt::set_static_extensions(split ' ', $Config{static_ext}); 195 chdir $build 196 or die "Couldn't chdir to '$build': $!"; # restore our start directory 197 } 198 199 my @ext; 200 push @ext, FindExt::static_ext() if $static; 201 push @ext, FindExt::dynamic_ext() if $dynamic; 202 push @ext, FindExt::nonxs_ext() if $nonxs; 203 push @ext, 'DynaLoader' if $dynaloader; 204 205 foreach (sort @ext) { 206 if (%incl and !exists $incl{$_}) { 207 #warn "Skipping extension $_, not in inclusion list\n"; 208 next; 209 } 210 if (exists $excl{$_}) { 211 warn "Skipping extension $_, not ported to current platform"; 212 next; 213 } 214 push @extspec, $_; 215 if($_ eq 'DynaLoader' and $target !~ /clean$/) { 216 # No, we don't know why nmake can't work out the dependency chain 217 push @{$extra_passthrough{$_}}, 'DynaLoader.c'; 218 } elsif(FindExt::is_static($_)) { 219 push @{$extra_passthrough{$_}}, 'LINKTYPE=static'; 220 } 221 } 222 223 chdir '..' 224 or die "Couldn't chdir to build directory: $!"; # now in the Perl build 225} 226elsif ($is_VMS) { 227 $perl = $^X; 228 push @extspec, (split ' ', $Config{static_ext}) if $static; 229 push @extspec, (split ' ', $Config{dynamic_ext}) if $dynamic; 230 push @extspec, (split ' ', $Config{nonxs_ext}) if $nonxs; 231 push @extspec, 'DynaLoader' if $dynaloader; 232} 233 234{ 235 # Cwd needs to be built before Encode recurses into subdirectories. 236 # This seems to be the simplest way to ensure this ordering: 237 my (@first, @other); 238 foreach (@extspec) { 239 if ($_ eq 'Cwd') { 240 push @first, $_; 241 } else { 242 push @other, $_; 243 } 244 } 245 @extspec = (@first, @other); 246} 247 248if ($Config{osname} eq 'catamount' and @extspec) { 249 # Snowball's chance of building extensions. 250 die "This is $Config{osname}, not building $extspec[0], sorry.\n"; 251} 252 253foreach my $spec (@extspec) { 254 my $mname = $spec; 255 $mname =~ s!/!::!g; 256 my $ext_pathname; 257 258 # Try new style ext/Data-Dumper/ first 259 my $copy = $spec; 260 $copy =~ tr!/!-!; 261 foreach my $dir (@ext_dirs) { 262 if (-d "$dir/$copy") { 263 $ext_pathname = "$dir/$copy"; 264 last; 265 } 266 } 267 268 if (!defined $ext_pathname) { 269 if (-d "ext/$spec") { 270 # Old style ext/Data/Dumper/ 271 $ext_pathname = "ext/$spec"; 272 } else { 273 warn "Can't find extension $spec in any of @ext_dirs"; 274 next; 275 } 276 } 277 278 print "\tMaking $mname ($target)\n"; 279 280 build_extension($ext_pathname, $perl, $mname, 281 [@pass_through, @{$extra_passthrough{$spec} || []}]); 282} 283 284sub build_extension { 285 my ($ext_dir, $perl, $mname, $pass_through) = @_; 286 287 unless (chdir "$ext_dir") { 288 warn "Cannot cd to $ext_dir: $!"; 289 return; 290 } 291 292 my $up = $ext_dir; 293 $up =~ s![^/]+!..!g; 294 295 $perl ||= "$up/miniperl"; 296 my $return_dir = $up; 297 my $lib_dir = "$up/lib"; 298 # $lib_dir must be last, as we're copying files into it, and in a parallel 299 # make there's a race condition if one process tries to open a module that 300 # another process has half-written. 301 my @new_inc = ((map {"$up/$_"} @toolchain), $lib_dir); 302 if ($is_Win32) { 303 @new_inc = map {File::Spec::Functions::rel2abs($_)} @new_inc; 304 } 305 $ENV{PERL5LIB} = join $Config{path_sep}, @new_inc; 306 $ENV{PERL_CORE} = 1; 307 # warn $ENV{PERL5LIB}; 308 309 my $makefile; 310 if ($is_VMS) { 311 $makefile = 'descrip.mms'; 312 if ($target =~ /clean$/ 313 && !-f $makefile 314 && -f "${makefile}_old") { 315 $makefile = "${makefile}_old"; 316 } 317 } else { 318 $makefile = 'Makefile'; 319 } 320 321 if (!-f $makefile) { 322 if (!-f 'Makefile.PL') { 323 print "\nCreating Makefile.PL in $ext_dir for $mname\n"; 324 # We need to cope well with various possible layouts 325 my @dirs = split /::/, $mname; 326 my $leaf = pop @dirs; 327 my $leafname = "$leaf.pm"; 328 my $pathname = join '/', @dirs, $leafname; 329 my @locations = ($leafname, $pathname, "lib/$pathname"); 330 my $fromname; 331 foreach (@locations) { 332 if (-f $_) { 333 $fromname = $_; 334 last; 335 } 336 } 337 338 unless ($fromname) { 339 die "For $mname tried @locations in in $ext_dir but can't find source"; 340 } 341 my $pod_name; 342 ($pod_name = $fromname) =~ s/\.pm\z/.pod/; 343 $pod_name = $fromname unless -e $pod_name; 344 open my $fh, '>', 'Makefile.PL' 345 or die "Can't open Makefile.PL for writing: $!"; 346 print $fh <<"EOM"; 347#-*- buffer-read-only: t -*- 348 349# This Makefile.PL was written by $0. 350# It will be deleted automatically by make realclean 351 352use strict; 353use ExtUtils::MakeMaker; 354 355WriteMakefile( 356 NAME => '$mname', 357 VERSION_FROM => '$fromname', 358 ABSTRACT_FROM => '$pod_name', 359 realclean => {FILES => 'Makefile.PL'}, 360); 361 362# ex: set ro: 363EOM 364 close $fh or die "Can't close Makefile.PL: $!"; 365 } 366 print "\nRunning Makefile.PL in $ext_dir\n"; 367 368 # Presumably this can be simplified 369 my @cross; 370 if (defined $::Cross::platform) { 371 # Inherited from win32/buildext.pl 372 @cross = "-MCross=$::Cross::platform"; 373 } elsif ($opts{cross}) { 374 # Inherited from make_ext.pl 375 @cross = '-MCross'; 376 } 377 378 my @args = (@cross, 'Makefile.PL'); 379 if ($is_VMS) { 380 my $libd = VMS::Filespec::vmspath($lib_dir); 381 push @args, "INST_LIB=$libd", "INST_ARCHLIB=$libd"; 382 } else { 383 push @args, 'INSTALLDIRS=perl', 'INSTALLMAN1DIR=none', 384 'INSTALLMAN3DIR=none'; 385 } 386 push @args, @$pass_through; 387 _quote_args(\@args) if $is_VMS; 388 print join(' ', @run, $perl, @args), "\n"; 389 my $code = system @run, $perl, @args; 390 warn "$code from $ext_dir\'s Makefile.PL" if $code; 391 392 # Right. The reason for this little hack is that we're sitting inside 393 # a program run by ./miniperl, but there are tasks we need to perform 394 # when the 'realclean', 'distclean' or 'veryclean' targets are run. 395 # Unfortunately, they can be run *after* 'clean', which deletes 396 # ./miniperl 397 # So we do our best to leave a set of instructions identical to what 398 # we would do if we are run directly as 'realclean' etc 399 # Whilst we're perfect, unfortunately the targets we call are not, as 400 # some of them rely on a $(PERL) for their own distclean targets. 401 # But this always used to be a problem with the old /bin/sh version of 402 # this. 403 if ($is_Unix) { 404 my $suffix = '.sh'; 405 foreach my $clean_target ('realclean', 'veryclean') { 406 my $file = "$return_dir/$clean_target$suffix"; 407 open my $fh, '>>', $file or die "open $file: $!"; 408 # Quite possible that we're being run in parallel here. 409 # Can't use Fcntl this early to get the LOCK_EX 410 flock $fh, 2 or warn "flock $file: $!"; 411 print $fh <<"EOS"; 412cd $ext_dir 413if test ! -f Makefile -a -f Makefile.old; then 414 echo "Note: Using Makefile.old" 415 make -f Makefile.old $clean_target MAKE='@make' @pass_through 416else 417 if test ! -f Makefile ; then 418 echo "Warning: No Makefile!" 419 fi 420 make $clean_target MAKE='@make' @pass_through 421fi 422cd $return_dir 423EOS 424 close $fh or die "close $file: $!"; 425 } 426 } 427 } 428 429 if (not -f $makefile) { 430 print "Warning: No Makefile!\n"; 431 } 432 433 if ($is_VMS) { 434 _macroify_passthrough($pass_through); 435 unshift @$pass_through, "/DESCRIPTION=$makefile"; 436 } 437 438 if (!$target or $target !~ /clean$/) { 439 # Give makefile an opportunity to rewrite itself. 440 # reassure users that life goes on... 441 my @args = ('config', @$pass_through); 442 _quote_args(\@args) if $is_VMS; 443 system(@run, @make, @args) and print "@run @make @args failed, continuing anyway...\n"; 444 } 445 my @targ = ($target, @$pass_through); 446 _quote_args(\@targ) if $is_VMS; 447 print "Making $target in $ext_dir\n@run @make @targ\n"; 448 my $code = system(@run, @make, @targ); 449 die "Unsuccessful make($ext_dir): code=$code" if $code != 0; 450 451 chdir $return_dir || die "Cannot cd to $return_dir: $!"; 452} 453 454sub _quote_args { 455 my $args = shift; # must be array reference 456 457 # Do not quote qualifiers that begin with '/'. 458 map { if (!/^\//) { 459 $_ =~ s/\"/""/g; # escape C<"> by doubling 460 $_ = q(").$_.q("); 461 } 462 } @{$args} 463 ; 464} 465 466sub _macroify_passthrough { 467 my $passthrough = shift; 468 _quote_args($passthrough); 469 my $macro = '/MACRO=(' . join(',',@$passthrough) . ')'; 470 @$passthrough = (); 471 @$passthrough[0] = $macro; 472} 473