1#!/usr/bin/perl 2 3use strict; 4use Test::More tests => 18; 5use Config; 6use DynaLoader; 7use ExtUtils::CBuilder; 8use lib (-d 't' ? File::Spec->catdir(qw(t lib)) : 'lib'); 9use PrimitiveCapture; 10 11my ($source_file, $obj_file, $lib_file); 12 13require_ok( 'ExtUtils::ParseXS' ); 14 15chdir('t') if -d 't'; 16push @INC, '.'; 17 18use Carp; $SIG{__WARN__} = \&Carp::cluck; 19 20# The linker on some platforms doesn't like loading libraries using relative 21# paths. Android won't find relative paths, and system perl on macOS will 22# refuse to load relative paths. The path that DynaLoader uses to load the 23# .so or .bundle file is based on the @INC path that the library is loaded 24# from. The XSTest module we're using for testing is in the current directory, 25# so we need an absolute path in @INC rather than '.'. Just convert all of the 26# paths to absolute for simplicity. 27@INC = map { File::Spec->rel2abs($_) } @INC; 28 29######################### 30 31{ # first block: try without linenumbers 32my $pxs = ExtUtils::ParseXS->new; 33# Try sending to filehandle 34tie *FH, 'Foo'; 35$pxs->process_file( filename => 'XSTest.xs', output => \*FH, prototypes => 1 ); 36like tied(*FH)->content, '/is_even/', "Test that output contains some text"; 37 38$source_file = 'XSTest.c'; 39 40# Try sending to file 41$pxs->process_file(filename => 'XSTest.xs', output => $source_file, prototypes => 0); 42ok -e $source_file, "Create an output file"; 43 44my $quiet = $ENV{PERL_CORE} && !$ENV{HARNESS_ACTIVE}; 45my $b = ExtUtils::CBuilder->new(quiet => $quiet); 46 47SKIP: { 48 skip "no compiler available", 2 49 if ! $b->have_compiler; 50 $obj_file = $b->compile( source => $source_file ); 51 ok $obj_file, "ExtUtils::CBuilder::compile() returned true value"; 52 ok -e $obj_file, "Make sure $obj_file exists"; 53} 54 55SKIP: { 56 skip "no dynamic loading", 5 57 if !$b->have_compiler || !$Config{usedl}; 58 my $module = 'XSTest'; 59 $lib_file = $b->link( objects => $obj_file, module_name => $module ); 60 ok $lib_file, "ExtUtils::CBuilder::link() returned true value"; 61 ok -e $lib_file, "Make sure $lib_file exists"; 62 63 eval {require XSTest}; 64 is $@, '', "No error message recorded, as expected"; 65 ok XSTest::is_even(8), 66 "Function created thru XS returned expected true value"; 67 ok !XSTest::is_even(9), 68 "Function created thru XS returned expected false value"; 69 70 # Win32 needs to close the DLL before it can unlink it, but unfortunately 71 # dl_unload_file was missing on Win32 prior to perl change #24679! 72 if ($^O eq 'MSWin32' and defined &DynaLoader::dl_unload_file) { 73 for (my $i = 0; $i < @DynaLoader::dl_modules; $i++) { 74 if ($DynaLoader::dl_modules[$i] eq $module) { 75 DynaLoader::dl_unload_file($DynaLoader::dl_librefs[$i]); 76 last; 77 } 78 } 79 } 80} 81 82my $seen = 0; 83open my $IN, '<', $source_file 84 or die "Unable to open $source_file: $!"; 85while (my $l = <$IN>) { 86 $seen++ if $l =~ m/#line\s1\s/; 87} 88is( $seen, 1, "Line numbers created in output file, as intended" ); 89{ 90 #rewind .c file and regexp it to look for code generation problems 91 local $/ = undef; 92 seek($IN, 0, 0); 93 my $filecontents = <$IN>; 94 my $good_T_BOOL_re = 95qr|\QXS_EUPXS(XS_XSTest_T_BOOL)\E 96.+? 97#line \d+\Q "XSTest.c" 98 ST(0) = boolSV(RETVAL); 99 } 100 XSRETURN(1); 101} 102\E|s; 103 like($filecontents, $good_T_BOOL_re, "T_BOOL doesn\'t have an extra sv_newmortal or sv_2mortal"); 104 105 my $good_T_BOOL_2_re = 106qr|\QXS_EUPXS(XS_XSTest_T_BOOL_2)\E 107.+? 108#line \d+\Q "XSTest.c" 109 sv_setsv(ST(0), boolSV(in)); 110 SvSETMAGIC(ST(0)); 111 } 112 XSRETURN(1); 113} 114\E|s; 115 like($filecontents, $good_T_BOOL_2_re, 'T_BOOL_2 doesn\'t have an extra sv_newmortal or sv_2mortal'); 116 my $good_T_BOOL_OUT_re = 117qr|\QXS_EUPXS(XS_XSTest_T_BOOL_OUT)\E 118.+? 119#line \d+\Q "XSTest.c" 120 sv_setsv(ST(0), boolSV(out)); 121 SvSETMAGIC(ST(0)); 122 } 123 XSRETURN_EMPTY; 124} 125\E|s; 126 like($filecontents, $good_T_BOOL_OUT_re, 'T_BOOL_OUT doesn\'t have an extra sv_newmortal or sv_2mortal'); 127 128} 129close $IN or die "Unable to close $source_file: $!"; 130 131unless ($ENV{PERL_NO_CLEANUP}) { 132 for ( $obj_file, $lib_file, $source_file) { 133 next unless defined $_; 134 1 while unlink $_; 135 } 136} 137} 138 139##################################################################### 140 141{ # second block: try with linenumbers 142my $pxs = ExtUtils::ParseXS->new; 143# Try sending to filehandle 144tie *FH, 'Foo'; 145$pxs->process_file( 146 filename => 'XSTest.xs', 147 output => \*FH, 148 prototypes => 1, 149 linenumbers => 0, 150); 151like tied(*FH)->content, '/is_even/', "Test that output contains some text"; 152 153$source_file = 'XSTest.c'; 154 155# Try sending to file 156$pxs->process_file( 157 filename => 'XSTest.xs', 158 output => $source_file, 159 prototypes => 0, 160 linenumbers => 0, 161); 162ok -e $source_file, "Create an output file"; 163 164 165my $seen = 0; 166open my $IN, '<', $source_file 167 or die "Unable to open $source_file: $!"; 168while (my $l = <$IN>) { 169 $seen++ if $l =~ m/#line\s1\s/; 170} 171close $IN or die "Unable to close $source_file: $!"; 172is( $seen, 0, "No linenumbers created in output file, as intended" ); 173 174unless ($ENV{PERL_NO_CLEANUP}) { 175 for ( $obj_file, $lib_file, $source_file) { 176 next unless defined $_; 177 1 while unlink $_; 178 } 179} 180} 181##################################################################### 182 183{ # third block: broken typemap 184my $pxs = ExtUtils::ParseXS->new; 185tie *FH, 'Foo'; 186my $stderr = PrimitiveCapture::capture_stderr(sub { 187 $pxs->process_file(filename => 'XSBroken.xs', output => \*FH); 188}); 189like $stderr, '/No INPUT definition/', "Exercise typemap error"; 190} 191##################################################################### 192 193sub Foo::TIEHANDLE { bless {}, 'Foo' } 194sub Foo::PRINT { shift->{buf} .= join '', @_ } 195sub Foo::content { shift->{buf} } 196