diff --git a/lib/ExtUtils/MM_Unix.pm b/lib/ExtUtils/MM_Unix.pm index 17a6bbfc9..898c1fdcb 100644 --- a/lib/ExtUtils/MM_Unix.pm +++ b/lib/ExtUtils/MM_Unix.pm @@ -398,6 +398,7 @@ sub constants { PERLRUNINST FULLPERLRUNINST ABSPERLRUNINST PERL_CORE PERM_DIR PERM_RW PERM_RWX + XSTARGET_EXT ) ) { @@ -1300,7 +1301,7 @@ sub init_dirscan { # --- File and Directory Lists (.xs .pm .pod etc) next if $self->{NORECURS}; $dir{$name} = $name if (-f $self->catfile($name,"Makefile.PL")); } elsif ($name =~ /\.xs\z/){ - my($c); ($c = $name) =~ s/\.xs\z/.c/; + my($c); ($c = $name) =~ s/\.xs\z/$self->{XSTARGET_EXT}/; $xs{$name} = $c; $c{$c} = 1; } elsif ($name =~ /\.c(pp|xx|c)?\z/i){ # .c .C .cpp .cxx .cc @@ -1755,6 +1756,9 @@ EOP $self->{LIBPERL_A} ||= "libperl$self->{LIB_EXT}"; + # Default extension for target produced from xs file + $self->{XSTARGET_EXT} ||= '.c'; + # make a simple check if we find strict warn "Warning: PERL_LIB ($self->{PERL_LIB}) seems not to be a perl library directory (strict.pm not found)" @@ -3644,7 +3648,8 @@ sub writedoc { =item xs_c (o) -Defines the suffix rules to compile XS files to C. +Defines the suffix rules to compile XS files to C (or to C++ +if XSTARGET_EXT set to ".cpp"). =cut @@ -3652,8 +3657,8 @@ sub xs_c { my($self) = shift; return '' unless $self->needs_linking(); ' -.xs.c: - $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $(XSUBPP_EXTRA_ARGS) $*.xs > $*.xsc && $(MV) $*.xsc $*.c +.xs$(XSTARGET_EXT): + $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $(XSUBPP_EXTRA_ARGS) $*.xs > $*.xsc && $(MV) $*.xsc $*$(XSTARGET_EXT) '; } @@ -3684,8 +3689,8 @@ sub xs_o { # many makes are too dumb to use xs_c then c_o return '' unless $self->needs_linking(); ' .xs$(OBJ_EXT): - $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $*.xs > $*.xsc && $(MV) $*.xsc $*.c - $(CCCMD) $(CCCDLFLAGS) -I$(PERL_INC) $(PASTHRU_DEFINE) $(DEFINE) $*.c + $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $*.xs > $*.xsc && $(MV) $*.xsc $*$(XSTARGET_EXT) + $(CCCMD) $(CCCDLFLAGS) -I$(PERL_INC) $(PASTHRU_DEFINE) $(DEFINE) $*$(XSTARGET_EXT) '; } diff --git a/lib/ExtUtils/MM_VMS.pm b/lib/ExtUtils/MM_VMS.pm index a31e466ad..be9fd4d56 100644 --- a/lib/ExtUtils/MM_VMS.pm +++ b/lib/ExtUtils/MM_VMS.pm @@ -905,8 +905,8 @@ sub xs_c { my($self) = @_; return '' unless $self->needs_linking(); ' -.xs.c : - $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $(MMS$TARGET_NAME).xs >$(MMS$TARGET) +.xs$(XSTARGET_EXT) : + $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $(MMS$TARGET_NAME).xs >$(MMS$TARGET_NAME)$(XSTARGET_EXT) '; } @@ -921,8 +921,8 @@ sub xs_o { # many makes are too dumb to use xs_c then c_o return '' unless $self->needs_linking(); ' .xs$(OBJ_EXT) : - $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $(MMS$TARGET_NAME).xs >$(MMS$TARGET_NAME).c - $(CCCMD) $(CCCDLFLAGS) $(MMS$TARGET_NAME).c + $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $(MMS$TARGET_NAME).xs >$(MMS$TARGET_NAME)$(XSTARGET_EXT) + $(CCCMD) $(CCCDLFLAGS) $(MMS$TARGET_NAME)$(XSTARGET_EXT) '; } diff --git a/lib/ExtUtils/MakeMaker.pm b/lib/ExtUtils/MakeMaker.pm index 56ce880af..5816fa40a 100644 --- a/lib/ExtUtils/MakeMaker.pm +++ b/lib/ExtUtils/MakeMaker.pm @@ -293,7 +293,7 @@ sub full_setup { PL_FILES PM PM_FILTER PMLIBDIRS PMLIBPARENTDIRS POLLUTE PREREQ_FATAL PREREQ_PM PREREQ_PRINT PRINT_PREREQ SIGN SKIP TEST_REQUIRES TYPEMAPS UNINST VERSION VERSION_FROM XS XSOPT XSPROTOARG - XS_VERSION clean depend dist dynamic_lib linkext macro realclean + XSTARGET_EXT XS_VERSION clean depend dist dynamic_lib linkext macro realclean tool_autosplit MAN1EXT MAN3EXT @@ -2800,6 +2800,11 @@ empty string is equivalent to the xsubpp default, or C<-noprototypes>. See the xsubpp documentation for details. MakeMaker defaults to the empty string. +=item XSTARGET_EXT + +Suffix for files produced from F<.xs> files. It is F<.c> by default, but can be +set to F<.cpp> if you xs code contains C++. + =item XS_VERSION Your version number for the .xs file of this package. This defaults diff --git a/lib/ExtUtils/MakeMaker/FAQ.pod b/lib/ExtUtils/MakeMaker/FAQ.pod index 9e95c3986..665d68341 100644 --- a/lib/ExtUtils/MakeMaker/FAQ.pod +++ b/lib/ExtUtils/MakeMaker/FAQ.pod @@ -446,6 +446,42 @@ And of course a very basic test: This tip has been brought to you by Nick Ing-Simmons and Stas Bekman. +=item How can I build my xs files as C++ + +Traditionally the language of source files is determined by suffix +(e.g. F<.c>- C, F<.cpp> or F<.cc> is C++), +so you need to tell MakeMaker that it should make files with an appropriate +suffix from your F<.xs> files. It can be done by using +C option. For example: + + Makefile.PL: + -------- + use ExtUtils::MakeMaker; + + WriteMakefile( + NAME => 'Foo', + VERSION_FROM => 'Foo.pm', + OBJECT => q/$(O_FILES)/, + XSTARGET_EXT => '.cpp', + XSOPT => '-C++ -nolinenumbers', + # ... other attrs ... + ); + +B: This solution is not crossplatform. Should work with gcc and clang, +but probably will require more tuning (e.g. will need linking with libstdc++). +Other modules that you may find helpful to configure your project to work +across a range of C++ compilers and platforms include: L, +L and L. + +B: If you have already generated F<.c> files before switching +the suffix, you need to remove these files manually, otherwise MakeMaker will +detect them and add to build. That will lead to strange errors from linker. + +B: You will probably need to disable file/line numbers info placed in +the generated file because C knows nothing about the generated file +suffix and uses F<.c> unconditionally. To do this, add C<-nolinenumbers> to +C option. + =back =head1 PATCHING diff --git a/t/lib/MakeMaker/Test/Setup/XSCPP.pm b/t/lib/MakeMaker/Test/Setup/XSCPP.pm new file mode 100644 index 000000000..8b168dd83 --- /dev/null +++ b/t/lib/MakeMaker/Test/Setup/XSCPP.pm @@ -0,0 +1,131 @@ +package MakeMaker::Test::Setup::XSCPP; + +@ISA = qw(Exporter); +require Exporter; +@EXPORT = qw(setup_xs teardown_xs); + +use strict; +use File::Path; +use File::Basename; +use MakeMaker::Test::Utils; + +my $Is_VMS = $^O eq 'VMS'; + +my %Files = ( + 'XSCPP-Test/lib/XSCPP/Test.pm' => <<'END', +package XSCPP::Test; + +require Exporter; +require DynaLoader; + +$VERSION = 1.01; +@ISA = qw(Exporter DynaLoader); +@EXPORT = qw(is_even); + +bootstrap XSCPP::Test $VERSION; + +1; +END + + 'XSCPP-Test/Makefile.PL' => <<'END', +use ExtUtils::MakeMaker; + +WriteMakefile( + NAME => 'XSCPP::Test', + VERSION_FROM => 'lib/XSCPP/Test.pm', + XSTARGET_EXT => '.cpp', + LIBS => ['-lstdc++'], +); +END + + 'XSCPP-Test/Test.xs' => <<'END', +extern "C" { +#include "EXTERN.h" +#include "perl.h" +} + +#include "XSUB.h" + +class CPPTest { + public: + CPPTest() { } + ~CPPTest() { } + int is_even(int num) { return (num % 2) == 0; } +}; + +MODULE = XSCPP::Test PACKAGE = XSCPP::Test + +PROTOTYPES: DISABLE + +CPPTest* +CPPTest::new(); + +int +CPPTest::is_even(int input); + +void +CPPTest::DESTROY(); + +END + + 'XSCPP-Test/typemap' => <<'END', +TYPEMAP +CPPTest * O_OBJECT + +OUTPUT + +O_OBJECT + sv_setref_pv( $arg, CLASS, (void*)$var ); + +INPUT + +O_OBJECT + if( sv_isobject($arg) && (SvTYPE(SvRV($arg)) == SVt_PVMG) ) + $var = ($type)SvIV((SV*)SvRV( $arg )); + else{ + warn( \"${Package}::$func_name() -- $var is not a blessed SV reference\" ); + XSRETURN_UNDEF; + } + +END + + 'XSCPP-Test/t/is_even.t' => <<'END', +#!/usr/bin/perl -w + +use Test::More tests => 3; + +use_ok "XSCPP::Test"; +my $o = XSCPP::Test->new; +ok !$o->is_even(1); +ok $o->is_even(2); +END + ); + + +sub setup_xs { + + while(my($file, $text) = each %Files) { + # Convert to a relative, native file path. + $file = File::Spec->catfile(File::Spec->curdir, split m{\/}, $file); + + my $dir = dirname($file); + mkpath $dir; + open(FILE, ">$file") || die "Can't create $file: $!"; + print FILE $text; + close FILE; + } + + return 1; +} + +sub teardown_xs { + foreach my $file (keys %Files) { + my $dir = dirname($file); + if( -e $dir ) { + rmtree($dir) || return; + } + } + return 1; +} + +1; diff --git a/t/lib/MakeMaker/Test/Utils.pm b/t/lib/MakeMaker/Test/Utils.pm index fd6cff202..f4b658e51 100644 --- a/t/lib/MakeMaker/Test/Utils.pm +++ b/t/lib/MakeMaker/Test/Utils.pm @@ -13,7 +13,7 @@ our $Is_FreeBSD = $^O eq 'freebsd'; our @EXPORT = qw(which_perl perl_lib makefile_name makefile_backup make make_run run make_macro calibrate_mtime - have_compiler slurp + have_compiler have_cplusplus slurp $Is_VMS $Is_MacOS run_ok ); @@ -348,23 +348,35 @@ Returns true if there is a compiler available for XS builds. sub have_compiler { my $have_compiler = 0; - # ExtUtils::CBuilder prints its compilation lines to the screen. - # Shut it up. - use TieOut; - local *STDOUT = *STDOUT; - local *STDERR = *STDERR; + eval { + require ExtUtils::CBuilder; + my $cb = ExtUtils::CBuilder->new(quiet=>1); + + $have_compiler = $cb->have_compiler; + }; + + return $have_compiler; +} - tie *STDOUT, 'TieOut'; - tie *STDERR, 'TieOut'; +=item have_cplusplus + + $have_compiler = have_cplusplus; + +Returns true if there is a C++ compiler available for XS builds. + +=cut + +sub have_cplusplus { + my $have_cplusplus = 0; eval { - require ExtUtils::CBuilder; - my $cb = ExtUtils::CBuilder->new; + require ExtUtils::CBuilder; + my $cb = ExtUtils::CBuilder->new(quiet=>1); - $have_compiler = $cb->have_compiler; + $have_cplusplus = $cb->have_cplusplus; }; - return $have_compiler; + return $have_cplusplus; } =item slurp diff --git a/t/xscpp.t b/t/xscpp.t new file mode 100644 index 000000000..36250e620 --- /dev/null +++ b/t/xscpp.t @@ -0,0 +1,60 @@ +#!/usr/bin/perl -w + +BEGIN { + unshift @INC, 't/lib/'; +} +chdir 't'; + +use strict; + +use MakeMaker::Test::Utils; +use MakeMaker::Test::Setup::XSCPP; + +# Disable full compilation test for now. +# TODO: Until some consensus in community will reached. +# use Test::More +# have_cplusplus() +# ? (tests => 5) +# : (skip_all => "ExtUtils::CBuilder not installed or couldn't find a C++ compiler"); +use Test::More tests => 5; +use File::Find; +use File::Spec; +use File::Path; + +my $Is_VMS = $^O eq 'VMS'; +my $perl = which_perl(); + +chdir 't'; + +perl_lib; + +$| = 1; + +ok( setup_xs(), 'setup' ); +END { + chdir File::Spec->updir or die; + teardown_xs(), 'teardown' or die; +} + +ok( chdir('XSCPP-Test'), "chdir'd to XSCPP-Test" ) || + diag("chdir failed: $!"); + +my @mpl_out = run(qq{$perl Makefile.PL}); + +cmp_ok( $?, '==', 0, 'Makefile.PL exited with zero' ) || + diag(@mpl_out); + +my $mf_content = slurp(makefile_name()); +like( $mf_content, qr/\bXSTARGET_EXT\b\s*=\s*\.cpp\b/, 'Makefile: XSTARGET_EXT has right value' ); +like( $mf_content, qr/\bXSUBPPRUN\b.+\bXSTARGET_EXT\b/, 'Makefile: seems xsubpp generate file with .cpp suffix' ); + +# Disable full compilation test for now. +# TODO: Until some consensus in community will reached. +# my $make = make_run(); +# my $make_out = run("$make"); +# is( $?, 0, ' make exited normally' ) || +# diag $make_out; +# +# my $test_out = run("$make test"); +# is( $?, 0, ' make test exited normally' ) || +# diag $test_out;