#!./miniperl
use
constant{
IS_CROSS
=>
defined
$Config::Config
{usecrosscompile} ? 1 : 0,
IS_WIN32
=> $^O eq
'MSWin32'
,
IS_VMS
=> $^O eq
'VMS'
,
IS_UNIX
=> $^O ne
'MSWin32'
&& $^O ne
'VMS'
,
};
my
@ext_dirs
=
qw(cpan dist ext)
;
my
$ext_dirs_re
=
'(?:'
.
join
(
'|'
,
@ext_dirs
) .
')'
;
my
(
%excl
,
%incl
,
%opts
,
@extspec
,
@pass_through
,
$verbose
);
foreach
(
@ARGV
) {
if
(/^!(.*)$/) {
$excl
{$1} = 1;
}
elsif
(/^\+(.*)$/) {
$incl
{$1} = 1;
}
elsif
(/^--verbose$/ or /^-v$/) {
$verbose
= 1;
}
elsif
(/^--([\w\-]+)$/) {
$opts
{$1} = 1;
}
elsif
(/^--([\w\-]+)=(.*)$/) {
push
@{
$opts
{$1}}, $2;
}
elsif
(/=/) {
push
@pass_through
,
$_
;
}
elsif
(
length
) {
push
@extspec
,
$_
;
}
}
my
$static
=
$opts
{static} ||
$opts
{all};
my
$dynamic
=
$opts
{dynamic} ||
$opts
{all};
my
$nonxs
=
$opts
{nonxs} ||
$opts
{all};
my
$dynaloader
=
$opts
{dynaloader} ||
$opts
{all};
foreach
(
@extspec
) {
if
(s{^lib/auto/}{}) {
s{/[^/]+\.[^/]+$}{};
}
elsif
(s{^
$ext_dirs_re
/}{}) {
s{/pm_to_blib$}{};
tr
!-!/!;
}
elsif
(s{::}{\/}g) {
}
else
{
s/\..
*o
//;
}
}
my
$makecmd
=
shift
@pass_through
;
unshift
@pass_through
,
'PERL_CORE=1'
;
my
@dirs
= @{
$opts
{dir} || \
@ext_dirs
};
my
$target
=
$opts
{target}[0];
$target
=
'all'
unless
defined
$target
;
unless
(
defined
$makecmd
and
$makecmd
=~ /^MAKE=(.*)$/) {
die
"$0: WARNING: Please include MAKE=\$(MAKE) in \@ARGV\n"
;
}
my
@make
=
split
' '
, $1 ||
$Config
{make} ||
$ENV
{MAKE};
if
(
$target
eq
''
) {
die
"make_ext: no make target specified (eg all or clean)\n"
;
}
elsif
(
$target
!~ /^(?:all|clean|distclean|realclean|veryclean)$/) {
die
"$0: unknown make target '$target'\n"
;
}
if
(!
@extspec
and !
$static
and !
$dynamic
and !
$nonxs
and !
$dynaloader
) {
die
"$0: no extension specified\n"
;
}
my
$perl
;
my
%extra_passthrough
;
if
(IS_WIN32) {
my
$build
= Cwd::getcwd();
$perl
= $^X;
if
(
$perl
=~ m
my
$here
=
$build
;
$here
=~ s{/}{\\}g;
$perl
=
"$here\\$perl"
;
}
(
my
$topdir
=
$perl
) =~ s/\\[^\\]+$//;
$ENV
{PATH} =
"$topdir;$topdir\\win32\\bin;$ENV{PATH}"
;
my
$pl2bat
=
"$topdir\\win32\\bin\\pl2bat"
;
unless
(-f
"$pl2bat.bat"
) {
my
@args
= (
$perl
,
"-I$topdir\\lib"
,
"-I$topdir\\cpan\\ExtUtils-PL2Bat\\lib"
, (
"$pl2bat.pl"
) x 2);
print
"@args\n"
if
$verbose
;
system
(
@args
)
unless
IS_CROSS;
}
print
"In $build"
if
$verbose
;
foreach
my
$dir
(
@dirs
) {
chdir
(
$dir
) or
die
"Cannot cd to $dir: $!\n"
;
(
my
$ext
= Cwd::getcwd()) =~ s{/}{\\}g;
FindExt::scan_ext(
$ext
);
FindExt::set_static_extensions(
split
' '
,
$Config
{static_ext});
chdir
$build
or
die
"Couldn't chdir to '$build': $!"
;
}
my
@ext
;
push
@ext
, FindExt::static_ext()
if
$static
;
push
@ext
, FindExt::dynamic_ext()
if
$dynamic
;
push
@ext
, FindExt::nonxs_ext()
if
$nonxs
;
push
@ext
,
'DynaLoader'
if
$dynaloader
;
foreach
(
sort
@ext
) {
if
(
%incl
and !
exists
$incl
{
$_
}) {
next
;
}
if
(
exists
$excl
{
$_
}) {
warn
"Skipping extension $_, not ported to current platform"
;
next
;
}
push
@extspec
,
$_
;
if
(
$_
ne
'DynaLoader'
&& FindExt::is_static(
$_
)) {
push
@{
$extra_passthrough
{
$_
}},
'LINKTYPE=static'
;
}
}
chdir
'..'
or
die
"Couldn't chdir to build directory: $!"
;
}
elsif
(IS_VMS) {
$perl
= $^X;
push
@extspec
, (
split
' '
,
$Config
{static_ext})
if
$static
;
push
@extspec
, (
split
' '
,
$Config
{dynamic_ext})
if
$dynamic
;
push
@extspec
, (
split
' '
,
$Config
{nonxs_ext})
if
$nonxs
;
push
@extspec
,
'DynaLoader'
if
$dynaloader
;
}
{
my
(
@first
,
@second
,
@other
);
foreach
(
@extspec
) {
if
(
$_
eq
'Cwd'
||
$_
eq
'if'
||
$_
eq
'lib'
) {
push
@first
,
$_
;
}
elsif
(
$_
eq
'Pod/Simple'
) {
push
@second
,
$_
;
}
else
{
push
@other
,
$_
;
}
}
@extspec
= (
@first
,
@second
,
@other
);
}
if
(
$Config
{osname} eq
'catamount'
and
@extspec
) {
die
"This is $Config{osname}, not building $extspec[0], sorry.\n"
;
}
$ENV
{PERL_CORE} = 1;
foreach
my
$spec
(
@extspec
) {
my
$mname
=
$spec
;
$mname
=~ s!/!::!g;
my
$ext_pathname
;
my
$copy
=
$spec
;
$copy
=~
tr
!/!-!;
$copy
=
'Scalar-List-Utils'
if
$copy
eq
'List-Util'
;
$copy
=
'PathTools'
if
$copy
eq
'Cwd'
;
foreach
my
$dir
(
@ext_dirs
) {
if
(-d
"$dir/$copy"
) {
$ext_pathname
=
"$dir/$copy"
;
last
;
}
}
if
(!
defined
$ext_pathname
) {
if
(-d
"ext/$spec"
) {
$ext_pathname
=
"ext/$spec"
;
}
else
{
warn
"Can't find extension $spec in any of @ext_dirs"
;
next
;
}
}
print
"\tMaking $mname ($target)\n"
if
$verbose
;
build_extension(
$ext_pathname
,
$perl
,
$mname
,
$target
,
[
@pass_through
, @{
$extra_passthrough
{
$spec
} || []}]);
}
sub
build_extension {
my
(
$ext_dir
,
$perl
,
$mname
,
$target
,
$pass_through
) =
@_
;
unless
(
chdir
"$ext_dir"
) {
warn
"Cannot cd to $ext_dir: $!"
;
return
;
}
my
$up
=
$ext_dir
;
$up
=~ s![^/]+!..!g;
$perl
||=
"$up/miniperl"
;
my
$return_dir
=
$up
;
my
$lib_dir
=
"$up/lib"
;
my
(
$makefile
,
$makefile_no_minus_f
);
if
(IS_VMS) {
$makefile
=
'descrip.mms'
;
if
(
$target
=~ /clean$/
&& !-f
$makefile
&& -f
"${makefile}_old"
) {
$makefile
=
"${makefile}_old"
;
}
}
else
{
$makefile
=
'Makefile'
;
}
if
(-f
$makefile
) {
$makefile_no_minus_f
= 0;
open
my
$mfh
,
'<'
,
$makefile
or
die
"Cannot open $makefile: $!"
;
while
(<
$mfh
>) {
last
if
/MakeMaker post_initialize section/;
next
unless
/^
my
$vmod
=
eval
$1;
my
$oldv
;
while
(<
$mfh
>) {
next
unless
/^XS_VERSION = (\S+)/;
$oldv
= $1;
last
;
}
last
unless
defined
$oldv
;
defined
(
my
$newv
= parse_version MM
$vmod
) or
last
;
if
(version->parse(
$newv
) ne
$oldv
) {
close
$mfh
or
die
"close $makefile: $!"
;
_unlink(
$makefile
);
{
no
warnings
'deprecated'
;
goto
NO_MAKEFILE;
}
}
}
if
(IS_CROSS) {
seek
(
$mfh
, 0, 0) or
die
"Cannot seek $makefile: $!"
;
my
$cross_makefile
;
while
(<
$mfh
>) {
if
(/^CC = \Q
$Config
{cc}\E/) {
$cross_makefile
= 1;
last
;
}
}
if
(!
$cross_makefile
) {
print
"Deleting non-Cross makefile\n"
;
close
$mfh
or
die
"close $makefile: $!"
;
_unlink(
$makefile
);
}
}
}
else
{
$makefile_no_minus_f
= 1;
}
if
(
$makefile_no_minus_f
|| !-f
$makefile
) {
NO_MAKEFILE:
if
(!-f
'Makefile.PL'
) {
unless
(just_pm_to_blib(
$target
,
$ext_dir
,
$mname
,
$return_dir
)) {
chdir
$return_dir
||
die
"Cannot cd to $return_dir: $!"
;
return
;
}
print
"\nCreating Makefile.PL in $ext_dir for $mname\n"
if
$verbose
;
my
(
$fromname
,
$key
,
$value
);
$key
=
'ABSTRACT_FROM'
;
my
@dirs
=
split
/::/,
$mname
;
my
$leaf
=
pop
@dirs
;
my
$leafname
=
"$leaf.pm"
;
my
$pathname
=
join
'/'
,
@dirs
,
$leafname
;
my
@locations
= (
$leafname
,
$pathname
,
"lib/$pathname"
);
foreach
(
@locations
) {
if
(-f
$_
) {
$fromname
=
$_
;
last
;
}
}
unless
(
$fromname
) {
die
"For $mname tried @locations in $ext_dir but can't find source"
;
}
(
$value
=
$fromname
) =~ s/\.pm\z/.pod/;
$value
=
$fromname
unless
-e
$value
;
if
(
$mname
eq
'Pod::Checker'
) {
$key
=
'ABSTRACT'
;
$value
=
'Pod::Checker verifies POD documentation contents for compliance with the POD format specifications'
;
}
open
my
$fh
,
'>'
,
'Makefile.PL'
or
die
"Can't open Makefile.PL for writing: $!"
;
printf
$fh
<<'EOM', $0, $mname, $fromname, $key, $value;
#-*- buffer-read-only: t -*-
# This Makefile.PL was written by %s.
# It will be deleted automatically by make realclean
use strict;
use ExtUtils::MakeMaker;
# This is what the .PL extracts to. Not the ultimate file that is installed.
# (ie Win32 runs pl2bat after this)
# Doing this here avoids all sort of quoting issues that would come from
# attempting to write out perl source with literals to generate the arrays and
# hash.
my @temps = 'Makefile.PL';
foreach (glob('scripts/pod*.PL')) {
# The various pod*.PL extractors change directory. Doing that with relative
# paths in @INC breaks. It seems the lesser of two evils to copy (to avoid)
# the chdir doing anything, than to attempt to convert lib paths to
# absolute, and potentially run into problems with quoting special
# characters in the path to our build dir (such as spaces)
require File::Copy;
my $temp = $_;
$temp =~ s!scripts/!!;
File::Copy::copy($_, $temp) or die "Can't copy $temp to $_: $!";
push @temps, $temp;
}
my $script_ext = $^O eq 'VMS' ? '.com' : '';
my %%pod_scripts;
foreach (glob('pod*.PL')) {
my $script = $_;
s/.PL$/$script_ext/i;
$pod_scripts{$script} = $_;
}
my @exe_files = values %%pod_scripts;
WriteMakefile(
NAME => '%s',
VERSION_FROM => '%s',
%-13s => '%s',
realclean => { FILES => "@temps" },
(%%pod_scripts ? (
PL_FILES => \%%pod_scripts,
EXE_FILES => \@exe_files,
clean => { FILES => "@exe_files" },
) : ()),
);
# ex: set ro:
EOM
close
$fh
or
die
"Can't close Makefile.PL: $!"
;
eval
{
my
$ftime
= (
stat
(
'Makefile.PL'
))[9] - 4;
utime
$ftime
,
$ftime
,
'Makefile.PL'
;
};
}
elsif
(
$mname
=~ /\A(?:Carp
|ExtUtils::CBuilder
|Safe
|Search::Dict)\z/x) {
my
(
$problem
) = just_pm_to_blib(
$target
,
$ext_dir
,
$mname
,
$return_dir
);
if
(
defined
$problem
) {
if
(-d
"$return_dir/.git"
) {
my
@files
= `git ls-files --cached --others --exclude-standard 2>/dev/null`;
foreach
(
@files
) {
chomp
;
die
"FATAL - $0 has $mname in the list of simple extensions, but it now contains file '$problem' which we can't handle"
if
$problem
eq
$_
;
}
}
warn
"WARNING - $0 is building $mname using EU::MM, as it found file '$problem'"
;
}
else
{
chdir
$return_dir
||
die
"Cannot cd to $return_dir: $!"
;
return
;
}
}
print
"\nRunning Makefile.PL in $ext_dir\n"
if
$verbose
;
my
@args
= (
"-I$lib_dir"
,
'Makefile.PL'
);
if
(IS_VMS) {
my
$libd
= VMS::Filespec::vmspath(
$lib_dir
);
push
@args
,
"INST_LIB=$libd"
,
"INST_ARCHLIB=$libd"
;
}
else
{
push
@args
,
'INSTALLDIRS=perl'
,
'INSTALLMAN1DIR=none'
,
'INSTALLMAN3DIR=none'
;
}
push
@args
,
@$pass_through
;
push
@args
,
'PERL='
.
$perl
if
$perl
;
_quote_args(\
@args
)
if
IS_VMS;
print
join
(
' '
,
$perl
,
@args
),
"\n"
if
$verbose
;
my
$code
=
do
{
local
$ENV
{PERL_MM_USE_DEFAULT} = 1;
system
$perl
,
@args
;
};
if
(
$code
!= 0){
_unlink(
$makefile
);
die
"Unsuccessful Makefile.PL($ext_dir): code=$code"
;
}
if
(IS_UNIX) {
foreach
my
$clean_target
(
'realclean'
,
'veryclean'
) {
fallback_cleanup(
$return_dir
,
$clean_target
,
<<"EOS");
cd $ext_dir
if test ! -f Makefile -a -f Makefile.old; then
echo "Note: Using Makefile.old"
make -f Makefile.old $clean_target MAKE='@make' @pass_through
else
if test ! -f Makefile ; then
echo "Warning: No Makefile!"
fi
@make $clean_target MAKE='@make' @pass_through
fi
cd $return_dir
EOS
}
}
}
if
(not -f
$makefile
) {
print
"Warning: No Makefile!\n"
;
}
if
(IS_VMS) {
_quote_args(
$pass_through
);
@$pass_through
= (
"/DESCRIPTION=$makefile"
,
'/MACRO=('
.
join
(
','
,
@$pass_through
) .
')'
);
}
my
@targ
= (
$target
,
@$pass_through
);
print
"Making $target in $ext_dir\n@make @targ\n"
if
$verbose
;
local
$ENV
{PERL_INSTALL_QUIET} = 1;
my
$code
=
system
(
@make
,
@targ
);
if
(
$code
>> 8 != 0){
$code
=
system
(
@make
,
@targ
);
}
die
"Unsuccessful make($ext_dir): code=$code"
if
$code
!= 0;
chdir
$return_dir
||
die
"Cannot cd to $return_dir: $!"
;
}
sub
_quote_args {
my
$args
=
shift
;
map
{
if
(!/^\//) {
$_
=~ s/\
"/"
"/g; # escape C<"
> by doubling
$_
=
q(")
.
$_
.
q(")
;
}
} @{
$args
}
;
}
sub
_unlink {
1
while
unlink
$_
[0];
my
$err
= $!;
die
"Can't unlink $_[0]: $err"
if
-f
$_
[0];
}
sub
just_pm_to_blib {
my
(
$target
,
$ext_dir
,
$mname
,
$return_dir
) =
@_
;
my
(
$has_lib
,
$has_top
,
$has_topdir
);
my
(
$last
) =
$mname
=~ /([^:]+)$/;
my
(
$first
) =
$mname
=~ /^([^:]+)/;
my
$pm_to_blib
= IS_VMS ?
'pm_to_blib.ts'
:
'pm_to_blib'
;
my
$silent
=
defined
$ENV
{MAKEFLAGS} &&
$ENV
{MAKEFLAGS} =~ /\b(s|silent|quiet)\b/;
foreach
my
$leaf
(<*>) {
if
(-d
$leaf
) {
$leaf
=~ s/\.DIR\z//i
if
IS_VMS;
next
if
$leaf
=~ /\A(?:\.|\.\.|t|demo)\z/;
if
(
$leaf
eq
'lib'
) {
++
$has_lib
;
next
;
}
if
(
$leaf
eq
$first
) {
++
$has_topdir
;
next
;
}
}
return
$leaf
unless
-f _;
$leaf
=~ s/\.\z//
if
IS_VMS;
next
if
$leaf
=~ /\A(ChangeLog
|Changes
|LICENSE
|Makefile\.PL
|MANIFEST
|META\.yml
|\Q
$pm_to_blib
\E
|README
|README\.patching
|README\.release
|\.gitignore
)\z/xi;
if
(
$leaf
eq
"$last.pm"
) {
++
$has_top
;
next
;
}
return
$leaf
;
}
return
'no lib/'
unless
$has_lib
||
$has_top
;
die
"Inconsistent module $mname has both lib/ and $first/"
if
$has_lib
&&
$has_topdir
;
print
"Running pm_to_blib for $ext_dir directly\n"
unless
$silent
;
my
%pm
;
if
(
$has_top
) {
my
$to
=
$mname
=~ s!::!/!gr;
$pm
{
"$last.pm"
} =
"../../lib/$to.pm"
;
}
if
(
$has_lib
||
$has_topdir
) {
my
@found
;
unless
(
eval
{
File::Find::find({
no_chdir
=> 1,
wanted
=>
sub
{
return
if
-d
$_
;
die
\
$_
unless
-f _;
die
\
$_
unless
/\A[^.]+\.(?:pm|pod)\z/i;
push
@found
,
$_
;
}
},
$has_lib
?
'lib'
:
$first
);
1;
}) {
return
${$@}
if
ref
$@ eq
'SCALAR'
;
die
$@;
}
if
(
$has_lib
) {
$pm
{
$_
} =
"../../$_"
foreach
@found
;
}
else
{
$pm
{
$_
} =
"../../lib/$_"
foreach
@found
;
}
}
if
(
$target
eq
'all'
) {
my
$need_update
= 1;
if
(-f
$pm_to_blib
) {
$need_update
= 0;
my
$test_at
= -M _;
while
(
my
$from
=
each
(
%pm
)) {
if
(-M
$from
<
$test_at
) {
++
$need_update
;
last
;
}
}
keys
%pm
;
}
if
(
$need_update
) {
local
$ENV
{PERL_INSTALL_QUIET} = 1;
ExtUtils::Install::pm_to_blib(\
%pm
,
'../../lib/auto'
);
open
my
$fh
,
'>'
,
$pm_to_blib
or
die
"Can't open '$pm_to_blib': $!"
;
print
$fh
"$0 has handled pm_to_blib directly\n"
;
close
$fh
or
die
"Can't close '$pm_to_blib': $!"
;
if
(IS_UNIX) {
my
$fallback
=
join
''
,
map
{s!^\.\./\.\./!!;
"rm -f $_\n"
}
sort
values
%pm
;
foreach
my
$clean_target
(
'realclean'
,
'veryclean'
) {
fallback_cleanup(
$return_dir
,
$clean_target
,
$fallback
);
}
}
}
}
else
{
_unlink(
$pm_to_blib
);
unless
(
$target
eq
'clean'
) {
_unlink(
$_
)
foreach
sort
values
%pm
;
}
}
return
;
}
sub
fallback_cleanup {
my
(
$dir
,
$clean_target
,
$contents
) =
@_
;
my
$file
=
"$dir/$clean_target.sh"
;
open
my
$fh
,
'>>'
,
$file
or
die
"open $file: $!"
;
flock
$fh
, 2 or
warn
"flock $file: $!"
;
print
$fh
$contents
or
die
"print $file: $!"
;
close
$fh
or
die
"close $file: $!"
;
}