summaryrefslogtreecommitdiff
path: root/make_ext.pl
diff options
context:
space:
mode:
authorNicholas Clark <nick@ccl4.org>2014-02-27 11:04:19 +0100
committerNicholas Clark <nick@ccl4.org>2014-03-02 07:08:54 +0100
commitf6dd0e4f7c748bb11d6531b5d95c0d83d7dbb395 (patch)
treeb5d212ebc6e7bba4e76f0eead987aeec42bc16be /make_ext.pl
parent3830cb7e9974e7471c7c3b0eb12e170a79dd55c3 (diff)
downloadperl-f6dd0e4f7c748bb11d6531b5d95c0d83d7dbb395.tar.gz
For simple extensions make_ext.pl can emulate the entire MakeMaker/make dance.
For simple extensions consisting only of Perl modules and Pod, EU::MM generates a Makefile where the only target that does any work as part of 'all' is 'pm_to_blib', and *that* is just running perl to call ExtUtils::Installed::pm_to_blib() with those files as arguments, and then touching pm_to_blib. Because the top level Makefile's dependency rule for "is an extension built" is looking for that file pm_to_blib, and all builds (and cleans) of extension directories are performed by running make_ext.pl, not a direct recursive make, it means that make_ext.pl can actually directly emulate the entire work done by EU::MM and make in this particular case. This means that we save (at least) three subprocesses: * miniperl to run Makefile.PL * make * miniperl to run ExtUtils::Installed::pm_to_blib() This change obviously adds some logic duplication, but it roughly halves the time taken for `make test_prep` to figure out that nothing needs doing. And obviously also saves at least that much time on the actual build, which may well be 60 seconds / number of cores.
Diffstat (limited to 'make_ext.pl')
-rw-r--r--make_ext.pl112
1 files changed, 109 insertions, 3 deletions
diff --git a/make_ext.pl b/make_ext.pl
index 3f8dad2732..0ab852c1fb 100644
--- a/make_ext.pl
+++ b/make_ext.pl
@@ -125,7 +125,8 @@ 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)$/) {
- # for the time being we are strict about what make_ext is used for
+ # we are strict about what make_ext is used for because we emulate these
+ # targets for simple modules:
die "$0: unknown make target '$target'\n";
}
@@ -253,12 +254,12 @@ foreach my $spec (@extspec) {
print "\tMaking $mname ($target)\n";
- build_extension($ext_pathname, $perl, $mname,
+ build_extension($ext_pathname, $perl, $mname, $target,
[@pass_through, @{$extra_passthrough{$spec} || []}]);
}
sub build_extension {
- my ($ext_dir, $perl, $mname, $pass_through) = @_;
+ my ($ext_dir, $perl, $mname, $target, $pass_through) = @_;
unless (chdir "$ext_dir") {
warn "Cannot cd to $ext_dir: $!";
@@ -341,6 +342,12 @@ sub build_extension {
if (!-f $makefile) {
NO_MAKEFILE:
if (!-f 'Makefile.PL') {
+ unless (just_pm_to_blib($target, $ext_dir)) {
+ # No problems returned, so it has faked everything for us. :-)
+ chdir $return_dir || die "Cannot cd to $return_dir: $!";
+ return;
+ }
+
print "\nCreating Makefile.PL in $ext_dir for $mname\n";
my ($fromname, $key, $value);
if ($mname eq 'podlators') {
@@ -532,6 +539,105 @@ sub _unlink {
die "Can't unlink $_[0]: $err" if -f $_[0];
}
+# Figure out if this extension is simple enough that it would only use
+# ExtUtils::MakeMaker's pm_to_blib target. If we're confident that it would,
+# then do all the work ourselves (returning an empty list), else return the
+# name of a file that we identified as beyond our ability to handle.
+#
+# While this is clearly quite a bit more work than just letting
+# ExtUtils::MakeMaker do it, and effectively is some code duplication, the time
+# savings are impressive.
+
+sub just_pm_to_blib {
+ my ($target, $ext_dir) = @_;
+ my $has_lib;
+ foreach my $leaf (<*>) {
+ if (-d $leaf) {
+ next if $leaf =~ /\A(?:\.|\.\.|t|demo)\z/;
+ if ($leaf eq 'lib') {
+ ++$has_lib;
+ next;
+ }
+ }
+ return $leaf
+ unless -f _;
+ # Makefile.PL is "safe" to ignore because we will only be called for
+ # directories that hold a Makefile.PL if they are in the exception list.
+ next
+ if $leaf =~ /\A(ChangeLog
+ |Changes
+ |LICENSE
+ |Makefile\.PL
+ |MANIFEST
+ |META\.yml
+ |pm_to_blib
+ |README
+ |README\.patching
+ |README\.release
+ )\z/xi; # /i to deal with case munging systems.
+ return $leaf;
+ }
+ return 'no lib/'
+ unless $has_lib;
+
+ print "\nRunning pm_to_blib for $ext_dir directly\n";
+
+ # strictly ExtUtils::MakeMaker uses the pm_to_blib target to install
+ # .pm, pod and .pl files. We're just going to do it for .pm and .pod
+ # files, to avoid problems on case munging file systems. Specifically,
+ # _pm.PL which ExtUtils::MakeMaker should run munges to _PM.PL, and
+ # looks a lot like a regular foo.pl (ie FOO.PL)
+ my @pm;
+ require File::Find;
+ unless (eval {
+ File::Find::find({
+ no_chdir => 1,
+ wanted => sub {
+ return if -d $_;
+ # Bail out immediately with the problem file:
+ die \$_
+ unless -f _;
+ die \$_
+ unless /\A[^.]+\.(?:pm|pod)\z/i;
+ push @pm, $_;
+ }
+ }, 'lib');
+ 1;
+ }) {
+ # Problem files aren't really errors:
+ return ${$@}
+ if ref $@ eq 'SCALAR';
+ # But anything else is:
+ die $@;
+ }
+ # This is running under miniperl, so no autodie
+ if ($target eq 'all') {
+ require ExtUtils::Install;
+ ExtUtils::Install::pm_to_blib({map {$_ => "../../$_"} sort @pm},
+ '../../lib/auto');
+ open my $fh, '>', 'pm_to_blib'
+ or die $!;
+ print $fh "$0 has handled pm_to_blib directly\n";
+ close $fh
+ or die $!;
+ } else {
+ # A clean target.
+ # For now, make the targets behave the same way as ExtUtils::MakeMaker
+ # does
+ _unlink('pm_to_blib');
+ unless ($target eq 'clean') {
+ # but cheat a bit, by relying on the top level Makefile clean target
+ # to take out our directory lib/auto/...
+ # (which it has to deal with, as cpan/foo/bar creates
+ # lib/auto/foo/bar, but the EU::MM rule will only
+ # rmdir lib/auto/foo/bar, leaving lib/auto/foo
+ _unlink("../../$_")
+ foreach @pm;
+ }
+ }
+ return;
+}
+
sub fallback_cleanup {
my ($dir, $clean_target, $contents) = @_;
my $file = "$dir/$clean_target.sh";