From: Graham Knop Date: Fri, 9 Oct 2015 10:19:39 +0000 (-0400) Subject: use ExtUtils::HasCompiler for compiler detection X-Git-Tag: v0.29~2 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=gitmo%2FClass-C3.git;a=commitdiff_plain;h=38787c2940b3ad3589a94822dd18782a5331b978 use ExtUtils::HasCompiler for compiler detection --- diff --git a/.gitignore b/.gitignore index 802f097..8e30be9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,12 +4,10 @@ /MYMETA.* /blib /build -/inc /pm_to_blib /MANIFEST /MANIFEST.bak /MANIFEST.SKIP /Distar -Debian* /Class-C3-*/ /Class-C3-*.tar.gz diff --git a/Changes b/Changes index 892d5f5..b01741d 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,7 @@ Revision history for Perl extension Class::C3. + - Update compiler detection to use ExtUtils::HasCompiler + 0.28 - 2015-04-14 - Change link to Dylan paper to use archive.org, as the original link has gone offline (RT#99756). diff --git a/Makefile.PL b/Makefile.PL index 7ca6203..2bbaa13 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,6 +1,8 @@ use strict; use warnings FATAL => 'all'; use 5.006; +use lib 'inc'; +use ExtUtils::HasCompiler qw(can_compile_loadable_object); my %META = ( name => 'Class-C3', @@ -8,7 +10,6 @@ my %META = ( prereqs => { configure => { requires => { 'ExtUtils::MakeMaker' => 0, - 'ExtUtils::CBuilder' => 0.27, } }, build => { requires => { } }, @@ -47,7 +48,7 @@ my %META = ( license => [ 'http://dev.perl.org/licenses/' ], }, no_index => { - directory => [ 't', 'xt', 'opt' ] + directory => [ 't', 'xt', 'opt', 'inc' ] }, ); @@ -57,56 +58,11 @@ my %MM_ARGS = ( ? ( 'Devel::Hide' => 0 ) : () }, PREREQ_PM => { - ( $] < 5.009_005 and can_xs() ) + ( $] < 5.009_005 and can_compile_loadable_object(quiet => 1) ) ? ( 'Class::C3::XS' => '0.13' ) : () }, ); -# Secondary compile testing via ExtUtils::CBuilder -sub can_xs { - # Do we have the configure_requires checker? - local $@; - eval "require ExtUtils::CBuilder;"; - if ( $@ ) { - # They don't obey configure_requires, so it is - # someone old and delicate. Try to avoid hurting - # them by falling back to an older simpler test. - return can_cc(); - } - - return ExtUtils::CBuilder->new( quiet => 1 )->have_compiler; -} - -# can we locate a (the) C compiler -sub can_cc { - my @chunks = split(/ /, $Config::Config{cc}) or return; - - # $Config{cc} may contain args; try to find out the program part - while (@chunks) { - return can_run("@chunks") || (pop(@chunks), next); - } - - return; -} - -# check if we can run some command -sub can_run { - my ($cmd) = @_; - - return $cmd if -x $cmd; - if (my $found_cmd = MM->maybe_command($cmd)) { - return $found_cmd; - } - - for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { - next if $dir eq ''; - my $abs = File::Spec->catfile($dir, $cmd); - return $abs if (-x $abs or $abs = MM->maybe_command($abs)); - } - - return; -} - sub is_smoker { return ( $ENV{AUTOMATED_TESTING} && ! $ENV{PERL5_CPANM_IS_RUNNING} && ! $ENV{RELEASE_TESTING} ) } diff --git a/inc/ExtUtils/HasCompiler.pm b/inc/ExtUtils/HasCompiler.pm new file mode 100644 index 0000000..06d4efc --- /dev/null +++ b/inc/ExtUtils/HasCompiler.pm @@ -0,0 +1,212 @@ +package ExtUtils::HasCompiler; +$ExtUtils::HasCompiler::VERSION = '0.012'; +use strict; +use warnings; + +use base 'Exporter'; +our @EXPORT_OK = qw/can_compile_loadable_object/; +our %EXPORT_TAGS = (all => \@EXPORT_OK); + +use Config; +use Carp 'carp'; +use File::Basename 'basename'; +use File::Spec::Functions qw/catfile catdir/; +use File::Temp qw/tempdir tempfile/; + +my $tempdir = tempdir(CLEANUP => 1); + +my $loadable_object_format = <<'END'; +#define PERL_NO_GET_CONTEXT +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#ifndef PERL_UNUSED_VAR +#define PERL_UNUSED_VAR(var) +#endif + +XS(exported) { +#ifdef dVAR + dVAR; +#endif + dXSARGS; + + PERL_UNUSED_VAR(cv); /* -W */ + PERL_UNUSED_VAR(items); /* -W */ + + XSRETURN_IV(42); +} + +#ifndef XS_EXTERNAL +#define XS_EXTERNAL(foo) XS(foo) +#endif + +/* we don't want to mess with .def files on mingw */ +#if defined(WIN32) && defined(__GNUC__) +# define EXPORT __declspec(dllexport) +#else +# define EXPORT +#endif + +EXPORT XS_EXTERNAL(boot_%s) { +#ifdef dVAR + dVAR; +#endif + dXSARGS; + + PERL_UNUSED_VAR(cv); /* -W */ + PERL_UNUSED_VAR(items); /* -W */ + + newXS("%s::exported", exported, __FILE__); +} + +END + +my $counter = 1; +my %prelinking = map { $_ => 1 } qw/MSWin32 VMS aix/; + +sub can_compile_loadable_object { + my %args = @_; + + my $config = $args{config} || 'ExtUtils::HasCompiler::Config'; + return if not $config->get('usedl'); + + my ($source_handle, $source_name) = tempfile(DIR => $tempdir, SUFFIX => '.c', UNLINK => 1); + my $basename = basename($source_name, '.c'); + + my $shortname = '_Loadable' . $counter++; + my $package = "ExtUtils::HasCompiler::$shortname"; + printf $source_handle $loadable_object_format, $basename, $package or do { carp "Couldn't write to $source_name: $!"; return }; + close $source_handle or do { carp "Couldn't close $source_name: $!"; return }; + + my $abs_basename = catfile($tempdir, $basename); + my $object_file = $abs_basename . $config->get('_o'); + my $loadable_object = $abs_basename . '.' . $config->get('dlext'); + my $incdir = catdir($config->get('archlibexp'), 'CORE'); + + my ($cc, $ccflags, $optimize, $cccdlflags, $ld, $ldflags, $lddlflags, $libperl, $perllibs) = map { $config->get($_) } qw/cc ccflags optimize cccdlflags ld ldflags lddlflags libperl perllibs/; + + if ($prelinking{$^O}) { + require ExtUtils::Mksymlists; + ExtUtils::Mksymlists::Mksymlists(NAME => $basename, FILE => $abs_basename, IMPORTS => {}); + } + my @commands; + if ($^O eq 'MSWin32' && $cc =~ /^cl/) { + push @commands, qq{$cc $ccflags $cccdlflags $optimize /I "$incdir" /c $source_name /Fo$object_file}; + push @commands, qq{$ld $object_file $lddlflags $libperl $perllibs /out:$loadable_object /def:$abs_basename.def /pdb:$abs_basename.pdb}; + } + elsif ($^O eq 'VMS') { + # Mksymlists is only the beginning of the story. + open my $opt_fh, '>>', "$abs_basename.opt" or do { carp "Couldn't append to '$abs_basename.opt'"; return }; + print $opt_fh "PerlShr/Share\n"; + close $opt_fh; + + my $incdirs = $ccflags =~ s{ /inc[^=]+ (?:=)+ (?:\()? ( [^\/\)]* ) }{}xi ? "$1,$incdir" : $incdir; + push @commands, qq{$cc $ccflags $optimize /include=($incdirs) $cccdlflags $source_name /obj=$object_file}; + push @commands, qq{$ld $ldflags $lddlflags=$loadable_object $object_file,$abs_basename.opt/OPTIONS,${incdir}perlshr_attr.opt/OPTIONS' $perllibs}; + } + else { + my @extra; + if ($^O eq 'MSWin32') { + push @extra, "$abs_basename.def"; + push @extra, '-l' . ($libperl =~ /lib([^.]+)\./)[0]; + } + elsif ($^O eq 'cygwin') { + push @extra, catfile($incdir, $config->get('useshrplib') ? 'libperl.dll.a' : 'libperl.a'); + } + elsif ($^O eq 'aix') { + $lddlflags =~ s/\Q$(BASEEXT)\E/$abs_basename/; + $lddlflags =~ s/\Q$(PERL_INC)\E/$incdir/; + } + push @commands, qq{$cc $ccflags $optimize "-I$incdir" $cccdlflags -c $source_name -o $object_file}; + push @commands, qq{$cc $optimize $object_file -o $loadable_object $lddlflags @extra $perllibs}; + } + + for my $command (@commands) { + print "$command\n" if not $args{quiet}; + system $command and do { carp "Couldn't execute $command: $!"; return }; + } + + # Skip loading when cross-compiling + return 1 if exists $args{skip_load} ? $args{skip_load} : $config->get('usecrosscompile'); + + require DynaLoader; + local @DynaLoader::dl_require_symbols = "boot_$basename"; + my $handle = DynaLoader::dl_load_file($loadable_object, 0); + if ($handle) { + my $symbol = DynaLoader::dl_find_symbol($handle, "boot_$basename") or do { carp "Couldn't find boot symbol for $basename"; return }; + my $compilet = DynaLoader::dl_install_xsub('__ANON__::__ANON__', $symbol, $source_name); + my $ret = eval { $compilet->(); $package->exported } or carp $@; + delete $ExtUtils::HasCompiler::{"$shortname\::"}; + eval { DynaLoader::dl_unload_file($handle) } or carp $@; + return defined $ret && $ret == 42; + } + else { + carp "Couldn't load $loadable_object: " . DynaLoader::dl_error(); + return; + } +} + +sub ExtUtils::HasCompiler::Config::get { + my (undef, $key) = @_; + return $ENV{uc $key} || $Config{$key}; +} + +1; + +# ABSTRACT: Check for the presence of a compiler + +__END__ + +=pod + +=encoding UTF-8 + +=head1 NAME + +ExtUtils::HasCompiler - Check for the presence of a compiler + +=head1 VERSION + +version 0.012 + +=head1 DESCRIPTION + +This module tries to check if the current system is capable of compiling, linking and loading an XS module. + +B: this is an early release, interface stability isn't guaranteed yet. + +=head1 FUNCTIONS + +=head2 can_compile_loadable_object(%opts) + +This checks if the system can compile, link and load a perl loadable object. It may take the following options: + +=over 4 + +=item * quiet + +Do not output the executed compilation commands. + +=item * config + +An L (compatible) object for configuration. + +=item * skip_load + +This causes can_compile_loadable_object to not try to load the generated object. This defaults to true on a cross-compiling perl. + +=back + +=head1 AUTHOR + +Leon Timmermans + +=head1 COPYRIGHT AND LICENSE + +This software is copyright (c) 2014 by Leon Timmermans. + +This is free software; you can redistribute it and/or modify it under +the same terms as the Perl 5 programming language system itself. + +=cut