4 use Test::More tests => 24;
7 use ExtUtils::CBuilder;
8 use lib (-d 't' ? File::Spec->catdir(qw(t lib)) : 'lib');
11 my ($source_file, $obj_file, $lib_file);
13 require_ok( 'ExtUtils::ParseXS' );
18 use Carp; $SIG{__WARN__} = \&Carp::cluck;
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;
29 #########################
31 { # first block: try without linenumbers
32 my $pxs = ExtUtils::ParseXS->new;
33 # Try sending to filehandle
35 $pxs->process_file( filename => 'XSTest.xs', output => \*FH, prototypes => 1 );
36 like tied(*FH)->content, '/is_even/', "Test that output contains some text";
38 $source_file = 'XSTest.c';
41 $pxs->process_file(filename => 'XSTest.xs', output => $source_file, prototypes => 0);
42 ok -e $source_file, "Create an output file";
44 my $quiet = $ENV{PERL_CORE} && !$ENV{HARNESS_ACTIVE};
45 my $b = ExtUtils::CBuilder->new(quiet => $quiet);
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";
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";
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";
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]);
83 open my $IN, '<', $source_file
84 or die "Unable to open $source_file: $!";
85 while (my $l = <$IN>) {
86 $seen++ if $l =~ m/#line\s1\s/;
88 is( $seen, 1, "Line numbers created in output file, as intended" );
90 #rewind .c file and regexp it to look for code generation problems
93 my $filecontents = <$IN>;
95 qr|\QXS_EUPXS(XS_XSTest_T_BOOL)\E
97 #line \d+\Q "XSTest.c"
98 ST(0) = boolSV(RETVAL);
103 like($filecontents, $good_T_BOOL_re, "T_BOOL doesn\'t have an extra sv_newmortal or sv_2mortal");
105 my $good_T_BOOL_2_re =
106 qr|\QXS_EUPXS(XS_XSTest_T_BOOL_2)\E
108 #line \d+\Q "XSTest.c"
109 sv_setsv(ST(0), boolSV(in));
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 =
117 qr|\QXS_EUPXS(XS_XSTest_T_BOOL_OUT)\E
119 #line \d+\Q "XSTest.c"
120 sv_setsv(ST(0), boolSV(out));
126 like($filecontents, $good_T_BOOL_OUT_re, 'T_BOOL_OUT doesn\'t have an extra sv_newmortal or sv_2mortal');
129 close $IN or die "Unable to close $source_file: $!";
131 unless ($ENV{PERL_NO_CLEANUP}) {
132 for ( $obj_file, $lib_file, $source_file) {
133 next unless defined $_;
139 #####################################################################
141 { # second block: try with linenumbers
142 my $pxs = ExtUtils::ParseXS->new;
143 # Try sending to filehandle
146 filename => 'XSTest.xs',
151 like tied(*FH)->content, '/is_even/', "Test that output contains some text";
153 $source_file = 'XSTest.c';
155 # Try sending to file
157 filename => 'XSTest.xs',
158 output => $source_file,
162 ok -e $source_file, "Create an output file";
166 open my $IN, '<', $source_file
167 or die "Unable to open $source_file: $!";
168 while (my $l = <$IN>) {
169 $seen++ if $l =~ m/#line\s1\s/;
171 close $IN or die "Unable to close $source_file: $!";
172 is( $seen, 0, "No linenumbers created in output file, as intended" );
174 unless ($ENV{PERL_NO_CLEANUP}) {
175 for ( $obj_file, $lib_file, $source_file) {
176 next unless defined $_;
181 #####################################################################
183 { # third block: broken typemap
184 my $pxs = ExtUtils::ParseXS->new;
186 my $stderr = PrimitiveCapture::capture_stderr(sub {
187 $pxs->process_file(filename => 'XSBroken.xs', output => \*FH);
189 like $stderr, '/No INPUT definition/', "Exercise typemap error";
191 #####################################################################
193 { # fourth block: https://github.com/Perl/perl5/issues/19661
194 my $pxs = ExtUtils::ParseXS->new;
196 my ($stderr, $filename);
198 $filename = 'XSFalsePositive.xs';
199 $stderr = PrimitiveCapture::capture_stderr(sub {
200 $pxs->process_file(filename => $filename, output => \*FH, prototypes => 1);
203 local $TODO = 'GH 19661';
205 qr/Warning: duplicate function definition 'do' detected in \Q$filename\E/,
206 "No 'duplicate function definition' warning observed in $filename";
210 $filename = 'XSFalsePositive2.xs';
211 $stderr = PrimitiveCapture::capture_stderr(sub {
212 $pxs->process_file(filename => $filename, output => \*FH, prototypes => 1);
215 local $TODO = 'GH 19661';
217 qr/Warning: duplicate function definition 'do' detected in \Q$filename\E/,
218 "No 'duplicate function definition' warning observed in $filename";
223 #####################################################################
225 { # tight cpp directives
226 my $pxs = ExtUtils::ParseXS->new;
228 my $stderr = PrimitiveCapture::capture_stderr(sub {
230 filename => 'XSTightDirectives.xs',
234 my $content = tied(*FH)->{buf};
236 $count++ while $content=~/^XS_EUPXS\(XS_My_do\)\n\{/mg;
237 is $stderr, undef, "No error expected from TightDirectives.xs";
238 is $count, 2, "Saw XS_MY_do definition the expected number of times";
242 my $pxs = ExtUtils::ParseXS->new;
244 my $stderr = PrimitiveCapture::capture_stderr(sub {
246 filename => 'XSAlias.xs',
250 my $content = tied(*FH)->{buf};
252 $count++ while $content=~/^XS_EUPXS\(XS_My_do\)\n\{/mg;
254 "Warning: Aliases 'pox' and 'dox', 'lox' have"
255 . " identical values in XSAlias.xs, line 9\n"
256 . " (If this is deliberate use a symbolic alias instead.)\n"
257 . "Warning: Conflicting duplicate alias 'pox' changes"
258 . " definition from '1' to '2' in XSAlias.xs, line 10\n"
259 . "Warning: Aliases 'docks' and 'dox', 'lox' have"
260 . " identical values in XSAlias.xs, line 11\n",
261 "Saw expected warnings from XSAlias.xs";
263 my $expect = quotemeta(<<'EOF_CONTENT');
264 cv = newXSproto_portable("My::dachs", XS_My_do, file, "$");
266 cv = newXSproto_portable("My::do", XS_My_do, file, "$");
268 cv = newXSproto_portable("My::docks", XS_My_do, file, "$");
270 cv = newXSproto_portable("My::dox", XS_My_do, file, "$");
272 cv = newXSproto_portable("My::lox", XS_My_do, file, "$");
274 cv = newXSproto_portable("My::pox", XS_My_do, file, "$");
277 $expect=~s/(?:\\[ ])+/\\s+/g;
279 like $content, $expect, "Saw expected alias initialization";
283 #####################################################################
285 sub Foo::TIEHANDLE { bless {}, 'Foo' }
286 sub Foo::PRINT { shift->{buf} .= join '', @_ }
287 sub Foo::content { shift->{buf} }