summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNicholas Clark <nick@ccl4.org>2002-08-25 19:06:00 +0100
committerhv <hv@crypt.org>2002-08-29 12:19:30 +0000
commit7783f9f6001b19735b378d8e18f3c5a6ac717876 (patch)
treefbf872b04ef7edff06f54395f4d95b862078a3b3 /lib
parent4750257bd21f5a4355221e101326277c013826ec (diff)
downloadperl-7783f9f6001b19735b378d8e18f3c5a6ac717876.tar.gz
ExtUtils::Constant 0.14
Message-ID: <20020825170600.GE322@Bagpuss.unfortu.net> p4raw-id: //depot/perl@17801
Diffstat (limited to 'lib')
-rw-r--r--lib/ExtUtils/Constant.pm71
-rw-r--r--lib/ExtUtils/t/Constant.t932
2 files changed, 606 insertions, 397 deletions
diff --git a/lib/ExtUtils/Constant.pm b/lib/ExtUtils/Constant.pm
index 0772ee8517..9730d91073 100644
--- a/lib/ExtUtils/Constant.pm
+++ b/lib/ExtUtils/Constant.pm
@@ -1,6 +1,6 @@
package ExtUtils::Constant;
use vars qw (@ISA $VERSION %XS_Constant %XS_TypeSet @EXPORT_OK %EXPORT_TAGS);
-$VERSION = '0.13';
+$VERSION = '0.14';
=head1 NAME
@@ -263,6 +263,11 @@ is equal to the C variable C<name>. If I<CHECKED_AT> is defined, then it
is used to avoid C<memEQ> for short names, or to generate a comment to
highlight the position of the character in the C<switch> statement.
+If I<CHECKED_AT> is a reference to a scalar, then instead it gives
+the characters pre-checked at the beginning, (and the number of chars by
+which the C variable name has been advanced. These need to be chopped from
+the front of I<NAME>).
+
=cut
sub memEQ_clause {
@@ -270,6 +275,14 @@ sub memEQ_clause {
# Which could actually be a character comparison or even ""
my ($name, $checked_at, $indent) = @_;
$indent = ' ' x ($indent || 4);
+ my $front_chop;
+ if (ref $checked_at) {
+ # regexp won't work on 5.6.1 without use utf8; in turn that won't work
+ # on 5.005_03.
+ substr ($name, 0, length $$checked_at,) = '';
+ $front_chop = C_stringify ($$checked_at);
+ undef $checked_at;
+ }
my $len = length $name;
if ($len < 2) {
@@ -289,12 +302,38 @@ sub memEQ_clause {
return $indent . "if (name[$check] == '$char') {\n";
}
}
- # Could optimise a memEQ on 3 to 2 single character checks here
+ if (($len == 2 and !defined $checked_at)
+ or ($len == 3 and defined ($checked_at) and $checked_at == 2)) {
+ my $char1 = C_stringify (substr $name, 0, 1);
+ my $char2 = C_stringify (substr $name, 1, 1);
+ return $indent . "if (name[0] == '$char1' && name[1] == '$char2') {\n";
+ }
+ if (($len == 3 and defined ($checked_at) and $checked_at == 1)) {
+ my $char1 = C_stringify (substr $name, 0, 1);
+ my $char2 = C_stringify (substr $name, 2, 1);
+ return $indent . "if (name[0] == '$char1' && name[2] == '$char2') {\n";
+ }
+
+ my $pointer = '^';
+ my $have_checked_last = defined ($checked_at) && $len == $checked_at + 1;
+ if ($have_checked_last) {
+ # Checked at the last character, so no need to memEQ it.
+ $pointer = C_stringify (chop $name);
+ $len--;
+ }
+
$name = C_stringify ($name);
my $body = $indent . "if (memEQ(name, \"$name\", $len)) {\n";
- $body .= $indent . "/* ". (' ' x $checked_at) . '^'
- . (' ' x ($len - $checked_at + length $len)) . " */\n"
- if defined $checked_at;
+ # Put a little ^ under the letter we checked at
+ # Screws up for non printable and non-7 bit stuff, but that's too hard to
+ # get right.
+ if (defined $checked_at) {
+ $body .= $indent . "/* ". (' ' x $checked_at) . $pointer
+ . (' ' x ($len - $checked_at + length $len)) . " */\n";
+ } elsif (defined $front_chop) {
+ $body .= $indent . "/* $front_chop"
+ . (' ' x ($len + 1 + length $len)) . " */\n";
+ }
return $body;
}
@@ -504,7 +543,9 @@ sub switch_clause {
# Figure out what to switch on.
# (RMS, Spread of jump table, Position, Hashref)
my @best = (1e38, ~0);
- foreach my $i (0 .. ($namelen - 1)) {
+ # Prefer the last character over the others. (As it lets us shortern the
+ # memEQ clause at no cost).
+ foreach my $i ($namelen - 1, 0 .. ($namelen - 2)) {
my ($min, $max) = (~0, 0);
my %spread;
if ($is_perl56) {
@@ -533,6 +574,8 @@ sub switch_clause {
# the string wins. Because if that passes but the memEQ fails, it may
# only need the start of the string to bin the choice.
# I think. But I'm micro-optimising. :-)
+ # OK. Trump that. Now favour the last character of the string, before the
+ # rest.
my $ss;
$ss += @$_ * @$_ foreach values %spread;
my $rms = sqrt ($ss / keys %spread);
@@ -540,12 +583,18 @@ sub switch_clause {
@best = ($rms, $max - $min, $i, \%spread);
}
}
- die "Internal error. Failed to pick a switch point for @names"
+ confess "Internal error. Failed to pick a switch point for @names"
unless defined $best[2];
# use Data::Dumper; print Dumper (@best);
my ($offset, $best) = @best[2,3];
$body .= $indent . "/* Offset $offset gives the best switch position. */\n";
- $body .= $indent . "switch (name[$offset]) {\n";
+
+ my $do_front_chop = $offset == 0 && $namelen > 2;
+ if ($do_front_chop) {
+ $body .= $indent . "switch (*name++) {\n";
+ } else {
+ $body .= $indent . "switch (name[$offset]) {\n";
+ }
foreach my $char (sort keys %$best) {
confess sprintf "'$char' is %d bytes long, not 1", length $char
if length ($char) != 1;
@@ -554,7 +603,11 @@ sub switch_clause {
foreach my $name (sort @{$best->{$char}}) {
my $thisone = $items->{$name};
# warn "You are here";
- $body .= match_clause ($thisone, $offset, 2 + length $indent);
+ if ($do_front_chop) {
+ $body .= match_clause ($thisone, \$char, 2 + length $indent);
+ } else {
+ $body .= match_clause ($thisone, $offset, 2 + length $indent);
+ }
}
$body .= $indent . " break;\n";
}
diff --git a/lib/ExtUtils/t/Constant.t b/lib/ExtUtils/t/Constant.t
index 6356ab45a6..4e5819d78e 100644
--- a/lib/ExtUtils/t/Constant.t
+++ b/lib/ExtUtils/t/Constant.t
@@ -1,7 +1,5 @@
#!/usr/bin/perl -w
-print "1..52\n";
-
BEGIN {
if( $ENV{PERL_CORE} ) {
chdir 't' if -d 't';
@@ -15,205 +13,333 @@ use ExtUtils::MakeMaker;
use ExtUtils::Constant qw (constant_types C_constant XS_constant autoload);
use Config;
use File::Spec;
+use Cwd;
my $do_utf_tests = $] > 5.006;
my $better_than_56 = $] > 5.007;
+# For debugging set this to 1.
+my $keep_files = 0;
+$| = 1;
# Because were are going to be changing directory before running Makefile.PL
my $perl = $^X;
# 5.005 doesn't have new enough File::Spec to have rel2abs. But actually we
# only need it when $^X isn't absolute, which is going to be 5.8.0 or later
# (where ExtUtils::Constant is in the core, and tests against the uninstalled
-# perl
+# perl)
$perl = File::Spec->rel2abs ($perl) unless $] < 5.006;
# ExtUtils::Constant::C_constant uses $^X inside a comment, and we want to
# compare output to ensure that it is the same. We were probably run as ./perl
# whereas we will run the child with the full path in $perl. So make $^X for
# us the same as our child will see.
$^X = $perl;
-
+my $lib = $ENV{PERL_CORE} ? '../../../lib' : '../../blib/lib';
+my $runperl = "$perl \"-I$lib\"";
print "# perl=$perl\n";
-my $lib = $ENV{PERL_CORE} ? '../../lib' : '../blib/lib';
-my $runperl = "$perl \"-I$lib\"";
+my $make = $Config{make};
+$make = $ENV{MAKE} if exists $ENV{MAKE};
+if ($^O eq 'MSWin32' && $make eq 'nmake') { $make .= " -nologo"; }
-$| = 1;
+# Renamed by make clean
+my $makefile = ($^O eq 'VMS' ? 'descrip' : 'Makefile');
+my $makefile_ext = ($^O eq 'VMS' ? '.mms' : '');
+my $makefile_rename = $makefile . ($^O eq 'VMS' ? '.mms' : '.old');
+my $output = "output";
+my $package = "ExtTest";
my $dir = "ext-$$";
-my @files;
+my $subdir = 0;
+# The real test counter.
+my $realtest = 1;
+
+my $orig_cwd = cwd;
+my $updir = File::Spec->updir;
+die "Can't get current directory: $!" unless defined $orig_cwd;
print "# $dir being created...\n";
mkdir $dir, 0777 or die "mkdir: $!\n";
-my $output = "output";
-
-# For debugging set this to 1.
-my $keep_files = 0;
-
END {
+ if (defined $orig_cwd and length $orig_cwd) {
+ chdir $orig_cwd or die "Can't chdir back to '$orig_cwd': $!";
use File::Path;
print "# $dir being removed...\n";
rmtree($dir) unless $keep_files;
+ } else {
+ # Can't get here.
+ die "cwd at start was empty, but directory '$dir' was created" if $dir;
+ }
}
-my $package = "ExtTest";
+chdir $dir or die $!;
+push @INC, '../../lib', '../../../lib';
-# Test the code that generates 1 and 2 letter name comparisons.
-my %compass = (
-N => 0, 'NE' => 45, E => 90, SE => 135, S => 180, SW => 225, W => 270, NW => 315
-);
+sub check_for_bonus_files {
+ my $dir = shift;
+ my %expect = map {($^O eq 'VMS' ? lc($_) : $_), 1} @_;
-my $parent_rfc1149 =
- 'A Standard for the Transmission of IP Datagrams on Avian Carriers';
-# Check that 8 bit and unicode names don't cause problems.
-my $pound;
-if (ord('A') == 193) { # EBCDIC platform
- $pound = chr 177; # A pound sign. (Currency)
-} else { # ASCII platform
- $pound = chr 163; # A pound sign. (Currency)
-}
+ my $fail;
+ opendir DIR, $dir or die "opendir '$dir': $!";
+ while (defined (my $entry = readdir DIR)) {
+ $entry =~ s/\.$// if $^O eq 'VMS'; # delete trailing dot that indicates no extension
+ next if $expect{$entry};
+ print "# Extra file '$entry'\n";
+ $fail = 1;
+ }
-my ($inf, $pound_bytes, $pound_utf8);
-if ($do_utf_tests) {
- $inf = chr 0x221E;
- # Check that we can distiguish the pathological case of a string, and the
- # utf8 representation of that string.
- $pound_utf8 = $pound . '1';
- if ($better_than_56) {
- $pound_bytes = $pound_utf8;
- utf8::encode ($pound_bytes);
+ closedir DIR or warn "closedir '.': $!";
+ if ($fail) {
+ print "not ok $realtest\n";
} else {
- # Must have that "U*" to generate a zero length UTF string that forces
- # top bit set chars (such as the pound sign) into UTF8, so that the
- # unpack 'C*' then gets the byte form of the UTF8.
- $pound_bytes = pack 'C*', unpack 'C*', $pound_utf8 . pack "U*";
+ print "ok $realtest\n";
}
+ $realtest++;
}
-my @names = ("FIVE", {name=>"OK6", type=>"PV",},
- {name=>"OK7", type=>"PVN",
- value=>['"not ok 7\\n\\0ok 7\\n"', 15]},
- {name => "FARTHING", type=>"NV"},
- {name => "NOT_ZERO", type=>"UV", value=>"~(UV)0"},
- {name => "OPEN", type=>"PV", value=>'"/*"', macro=>1},
- {name => "CLOSE", type=>"PV", value=>'"*/"',
- macro=>["#if 1\n", "#endif\n"]},
- {name => "ANSWER", default=>["UV", 42]}, "NOTDEF",
- {name => "Yes", type=>"YES"},
- {name => "No", type=>"NO"},
- {name => "Undef", type=>"UNDEF"},
-# OK. It wasn't really designed to allow the creation of dual valued constants.
-# It was more for INADDR_ANY INADDR_BROADCAST INADDR_LOOPBACK INADDR_NONE
- {name=>"RFC1149", type=>"SV", value=>"sv_2mortal(temp_sv)",
- pre=>"SV *temp_sv = newSVpv(RFC1149, 0); "
- . "(void) SvUPGRADE(temp_sv,SVt_PVIV); SvIOK_on(temp_sv); "
- . "SvIVX(temp_sv) = 1149;"},
- {name=>"perl", type=>"PV",},
-);
+sub build_and_run {
+ my ($tests, $expect, $files) = @_;
+ my $core = $ENV{PERL_CORE} ? ' PERL_CORE=1' : '';
+ my @perlout = `$runperl Makefile.PL $core`;
+ if ($?) {
+ print "not ok $realtest # $runperl Makefile.PL failed: $?\n";
+ print "# $_" foreach @perlout;
+ exit($?);
+ } else {
+ print "ok $realtest\n";
+ }
+ $realtest++;
-push @names, $_ foreach keys %compass;
+ if (-f "$makefile$makefile_ext") {
+ print "ok $realtest\n";
+ } else {
+ print "not ok $realtest\n";
+ }
+ $realtest++;
-# Automatically compile the list of all the macro names, and make them
-# exported constants.
-my @names_only = map {(ref $_) ? $_->{name} : $_} @names;
+ my @makeout;
-# Exporter::Heavy (currently) isn't able to export these names:
-push @names, ({name=>"*/", type=>"PV", value=>'"CLOSE"', macro=>1},
- {name=>"/*", type=>"PV", value=>'"OPEN"', macro=>1},
- {name=>$pound, type=>"PV", value=>'"Sterling"', macro=>1},
- );
+ if ($^O eq 'VMS') { $make .= ' all'; }
-if ($do_utf_tests) {
- push @names, ({name=>$inf, type=>"PV", value=>'"Infinity"', macro=>1},
- {name=>$pound_utf8, type=>"PV", value=>'"1 Pound"', macro=>1},
- {name=>$pound_bytes, type=>"PV", value=>'"1 Pound (as bytes)"',
- macro=>1},
- );
-}
+ print "# make = '$make'\n";
+ @makeout = `$make`;
+ if ($?) {
+ print "not ok $realtest # $make failed: $?\n";
+ print "# $_" foreach @makeout;
+ exit($?);
+ } else {
+ print "ok $realtest\n";
+ }
+ $realtest++;
-=pod
+ if ($^O eq 'VMS') { $make =~ s{ all}{}; }
-The above set of names seems to produce a suitably bad set of compile
-problems on a Unicode naive version of ExtUtils::Constant (ie 0.11):
+ if ($Config{usedl}) {
+ print "ok $realtest # This is dynamic linking, so no need to make perl\n";
+ } else {
+ my $makeperl = "$make perl";
+ print "# make = '$makeperl'\n";
+ @makeout = `$makeperl`;
+ if ($?) {
+ print "not ok $realtest # $makeperl failed: $?\n";
+ print "# $_" foreach @makeout;
+ exit($?);
+ } else {
+ print "ok $realtest\n";
+ }
+ }
+ $realtest++;
-nick@thinking-cap 15439-32-utf$ PERL_CORE=1 ./perl lib/ExtUtils/t/Constant.t
-1..33
-# perl=/stuff/perl5/15439-32-utf/perl
-# ext-30370 being created...
-Wide character in print at lib/ExtUtils/t/Constant.t line 140.
-ok 1
-ok 2
-# make = 'make'
-ExtTest.xs: In function `constant_1':
-ExtTest.xs:80: warning: multi-character character constant
-ExtTest.xs:80: warning: case value out of range
-ok 3
+ my $maketest = "$make test";
+ print "# make = '$maketest'\n";
-=cut
+ @makeout = `$maketest`;
-# Grr `
+ if (open OUTPUT, "<$output") {
+ local $/; # Slurp it - faster.
+ print <OUTPUT>;
+ close OUTPUT or print "# Close $output failed: $!\n";
+ } else {
+ # Harness will report missing test results at this point.
+ print "# Open <$output failed: $!\n";
+ }
-my $types = {};
-my $constant_types = constant_types(); # macro defs
-my $C_constant = join "\n",
- C_constant ($package, undef, "IV", $types, undef, undef, @names);
-my $XS_constant = XS_constant ($package, $types); # XS for ExtTest::constant
-
-################ Header
-my $header = File::Spec->catdir($dir, "test.h");
-push @files, "test.h";
-open FH, ">$header" or die "open >$header: $!\n";
-print FH <<"EOT";
-#define FIVE 5
-#define OK6 "ok 6\\n"
-#define OK7 1
-#define FARTHING 0.25
-#define NOT_ZERO 1
-#define Yes 0
-#define No 1
-#define Undef 1
-#define RFC1149 "$parent_rfc1149"
-#undef NOTDEF
-#define perl "rules"
+ $realtest += $tests;
+ if ($?) {
+ print "not ok $realtest # $maketest failed: $?\n";
+ print "# $_" foreach @makeout;
+ } else {
+ print "ok $realtest - maketest\n";
+ }
+ $realtest++;
+
+ # -x is busted on Win32 < 5.6.1, so we emulate it.
+ my $regen;
+ if( $^O eq 'MSWin32' && $] <= 5.006001 ) {
+ open(REGENTMP, ">regentmp") or die $!;
+ open(XS, "$package.xs") or die $!;
+ my $saw_shebang;
+ while(<XS>) {
+ $saw_shebang++ if /^#!.*/i ;
+ print REGENTMP $_ if $saw_shebang;
+ }
+ close XS; close REGENTMP;
+ $regen = `$runperl regentmp`;
+ unlink 'regentmp';
+ }
+ else {
+ $regen = `$runperl -x $package.xs`;
+ }
+ if ($?) {
+ print "not ok $realtest # $runperl -x $package.xs failed: $?\n";
+ } else {
+ print "ok $realtest - regen\n";
+ }
+ $realtest++;
+
+ if ($expect eq $regen) {
+ print "ok $realtest - regen worked\n";
+ } else {
+ print "not ok $realtest - regen worked\n";
+ # open FOO, ">expect"; print FOO $expect;
+ # open FOO, ">regen"; print FOO $regen; close FOO;
+ }
+ $realtest++;
+
+ my $makeclean = "$make clean";
+ print "# make = '$makeclean'\n";
+ @makeout = `$makeclean`;
+ if ($?) {
+ print "not ok $realtest # $make failed: $?\n";
+ print "# $_" foreach @makeout;
+ } else {
+ print "ok $realtest\n";
+ }
+ $realtest++;
+
+ check_for_bonus_files ('.', @$files, $output, $makefile_rename, '.', '..');
+
+ rename $makefile_rename, $makefile
+ or die "Can't rename '$makefile_rename' to '$makefile': $!";
+
+ unlink $output or warn "Can't unlink '$output': $!";
+
+ # Need to make distclean to remove ../../lib/ExtTest.pm
+ my $makedistclean = "$make distclean";
+ print "# make = '$makedistclean'\n";
+ @makeout = `$makedistclean`;
+ if ($?) {
+ print "not ok $realtest # $make failed: $?\n";
+ print "# $_" foreach @makeout;
+ } else {
+ print "ok $realtest\n";
+ }
+ $realtest++;
+
+ check_for_bonus_files ('.', @$files, '.', '..');
+
+ unless ($keep_files) {
+ foreach (@$files) {
+ unlink $_ or warn "unlink $_: $!";
+ }
+ }
+
+ check_for_bonus_files ('.', '.', '..');
+}
+
+sub Makefile_PL {
+ my $package = shift;
+ ################ Makefile.PL
+ # We really need a Makefile.PL because make test for a no dynamic linking perl
+ # will run Makefile.PL again as part of the "make perl" target.
+ my $makefilePL = "Makefile.PL";
+ open FH, ">$makefilePL" or die "open >$makefilePL: $!\n";
+ print FH <<"EOT";
+#!$perl -w
+use ExtUtils::MakeMaker;
+WriteMakefile(
+ 'NAME' => "$package",
+ 'VERSION_FROM' => "$package.pm", # finds \$VERSION
+ (\$] >= 5.005 ?
+ (#ABSTRACT_FROM => "$package.pm", # XXX add this
+ AUTHOR => "$0") : ())
+ );
EOT
-while (my ($point, $bearing) = each %compass) {
- print FH "#define $point $bearing\n"
+ close FH or die "close $makefilePL: $!\n";
+ return $makefilePL;
+}
+
+sub MANIFEST {
+ my (@files) = @_;
+ ################ MANIFEST
+ # We really need a MANIFEST because make distclean checks it.
+ my $manifest = "MANIFEST";
+ push @files, $manifest;
+ open FH, ">$manifest" or die "open >$manifest: $!\n";
+ print FH "$_\n" foreach @files;
+ close FH or die "close $manifest: $!\n";
+ return @files;
}
-close FH or die "close $header: $!\n";
-################ XS
-my $xs = File::Spec->catdir($dir, "$package.xs");
-push @files, "$package.xs";
-open FH, ">$xs" or die "open >$xs: $!\n";
+sub write_and_run_extension {
+ my ($name, $items, $export_names, $package, $header, $testfile, $num_tests)
+ = @_;
+ my $types = {};
+ my $constant_types = constant_types(); # macro defs
+ my $C_constant = join "\n",
+ C_constant ($package, undef, "IV", $types, undef, undef, @$items);
+ my $XS_constant = XS_constant ($package, $types); # XS for ExtTest::constant
+
+ my $expect = $constant_types . $C_constant .
+ "\n#### XS Section:\n" . $XS_constant;
+
+ print "# $name\n# $dir/$subdir being created...\n";
+ mkdir $subdir, 0777 or die "mkdir: $!\n";
+ chdir $subdir or die $!;
-print FH <<'EOT';
+ my @files;
+
+ ################ Header
+ my $header_name = "test.h";
+ push @files, $header_name;
+ open FH, ">$header_name" or die "open >$header_name: $!\n";
+ print FH $header or die $!;
+ close FH or die "close $header_name: $!\n";
+
+ ################ XS
+ my $xs = "$package.xs";
+ push @files, $xs;
+ open FH, ">$xs" or die "open >$xs: $!\n";
+
+ print FH <<'EOT';
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
EOT
-print FH "#include \"test.h\"\n\n";
-print FH $constant_types;
-print FH $C_constant, "\n";
-print FH "MODULE = $package PACKAGE = $package\n";
-print FH "PROTOTYPES: ENABLE\n";
-print FH $XS_constant;
-close FH or die "close $xs: $!\n";
-
-################ PM
-my $pm = File::Spec->catdir($dir, "$package.pm");
-push @files, "$package.pm";
-open FH, ">$pm" or die "open >$pm: $!\n";
-print FH "package $package;\n";
-print FH "use $];\n";
+ # XXX Here doc these:
+ print FH "#include \"$header_name\"\n\n";
+ print FH $constant_types;
+ print FH $C_constant, "\n";
+ print FH "MODULE = $package PACKAGE = $package\n";
+ print FH "PROTOTYPES: ENABLE\n";
+ print FH $XS_constant;
+ close FH or die "close $xs: $!\n";
+
+ ################ PM
+ my $pm = "$package.pm";
+ push @files, $pm;
+ open FH, ">$pm" or die "open >$pm: $!\n";
+ print FH "package $package;\n";
+ print FH "use $];\n";
-print FH <<'EOT';
+ print FH <<'EOT';
use strict;
EOT
-printf FH "use warnings;\n" unless $] < 5.006;
-print FH <<'EOT';
+ printf FH "use warnings;\n" unless $] < 5.006;
+ print FH <<'EOT';
use Carp;
require Exporter;
@@ -222,50 +348,156 @@ use vars qw ($VERSION @ISA @EXPORT_OK $AUTOLOAD);
$VERSION = '0.01';
@ISA = qw(Exporter DynaLoader);
-@EXPORT_OK = qw(
EOT
+ # Having this qw( in the here doc confuses cperl mode far too much to be
+ # helpful. And I'm using cperl mode to edit this, even if you're not :-)
+ print FH "\@EXPORT_OK = qw(\n";
+
+ # Print the names of all our autoloaded constants
+ print FH "\t$_\n" foreach (@$export_names);
+ print FH ");\n";
+ # Print the AUTOLOAD subroutine ExtUtils::Constant generated for us
+ print FH autoload ($package, $]);
+ print FH "bootstrap $package \$VERSION;\n1;\n__END__\n";
+ close FH or die "close $pm: $!\n";
+
+ ################ test.pl
+ my $testpl = "test.pl";
+ push @files, $testpl;
+ open FH, ">$testpl" or die "open >$testpl: $!\n";
+ # Standard test header (need an option to suppress this?)
+ print FH <<"EOT" or die $!;
+use strict;
+use $package qw(@$export_names);
-# Print the names of all our autoloaded constants
-print FH "\t$_\n" foreach (@names_only);
-print FH ");\n";
-# Print the AUTOLOAD subroutine ExtUtils::Constant generated for us
-print FH autoload ($package, $]);
-print FH "bootstrap $package \$VERSION;\n1;\n__END__\n";
-close FH or die "close $pm: $!\n";
-
-################ test.pl
-my $testpl = File::Spec->catdir($dir, "test.pl");
-push @files, "test.pl";
-open FH, ">$testpl" or die "open >$testpl: $!\n";
-
-print FH "use strict;\n";
-print FH "use $package qw(@names_only);\n\n";
-
-print FH "use utf8\n\n" if $do_utf_tests;
-
-print FH <<"EOT";
-
-print "1..1\n";
+print "1..2\n";
if (open OUTPUT, ">$output") {
print "ok 1\n";
select OUTPUT;
} else {
- print "not ok 1 # Failed to open '$output': $!\n";
+ print "not ok 1 # Failed to open '$output': \$!\n";
exit 1;
}
EOT
+ print FH $testfile or die $!;
+ print FH <<"EOT" or die $!;
+select STDOUT;
+if (close OUTPUT) {
+ print "ok 2\n";
+} else {
+ print "not ok 2 # Failed to close '$output': \$!\n";
+}
+EOT
+ close FH or die "close $testpl: $!\n";
-print FH << 'EOT';
+ push @files, Makefile_PL($package);
+ @files = MANIFEST (@files);
-my $better_than_56 = $] > 5.007;
+ build_and_run ($num_tests, $expect, \@files);
+
+ chdir $updir or die "chdir '$updir': $!";
+ ++$subdir;
+}
+# Tests are arrayrefs of the form
+# $name, [items], [export_names], $package, $header, $testfile, $num_tests
+my @tests;
+my $before_tests = 4; # Number of "ok"s emitted to build extension
+my $after_tests = 8; # Number of "ok"s emitted after make test run
+my $dummytest = 1;
+
+my $here;
+sub start_tests {
+ $dummytest += $before_tests;
+ $here = $dummytest;
+}
+sub end_tests {
+ my ($name, $items, $export_names, $header, $testfile) = @_;
+ push @tests, [$name, $items, $export_names, $package, $header, $testfile,
+ $dummytest - $here];
+ $dummytest += $after_tests;
+}
+
+my $pound;
+if (ord('A') == 193) { # EBCDIC platform
+ $pound = chr 177; # A pound sign. (Currency)
+} else { # ASCII platform
+ $pound = chr 163; # A pound sign. (Currency)
+}
+my @common_items = (
+ {name=>"perl", type=>"PV",},
+ {name=>"*/", type=>"PV", value=>'"CLOSE"', macro=>1},
+ {name=>"/*", type=>"PV", value=>'"OPEN"', macro=>1},
+ {name=>$pound, type=>"PV", value=>'"Sterling"', macro=>1},
+ );
+
+{
+ # Simple tests
+ start_tests();
+ my $parent_rfc1149 =
+ 'A Standard for the Transmission of IP Datagrams on Avian Carriers';
+ # Test the code that generates 1 and 2 letter name comparisons.
+ my %compass = (
+ N => 0, 'NE' => 45, E => 90, SE => 135,
+ S => 180, SW => 225, W => 270, NW => 315
+ );
+
+ my $header = << "EOT";
+#define FIVE 5
+#define OK6 "ok 6\\n"
+#define OK7 1
+#define FARTHING 0.25
+#define NOT_ZERO 1
+#define Yes 0
+#define No 1
+#define Undef 1
+#define RFC1149 "$parent_rfc1149"
+#undef NOTDEF
+#define perl "rules"
+EOT
+
+ while (my ($point, $bearing) = each %compass) {
+ $header .= "#define $point $bearing\n"
+ }
+ my @items = ("FIVE", {name=>"OK6", type=>"PV",},
+ {name=>"OK7", type=>"PVN",
+ value=>['"not ok 7\\n\\0ok 7\\n"', 15]},
+ {name => "FARTHING", type=>"NV"},
+ {name => "NOT_ZERO", type=>"UV", value=>"~(UV)0"},
+ {name => "OPEN", type=>"PV", value=>'"/*"', macro=>1},
+ {name => "CLOSE", type=>"PV", value=>'"*/"',
+ macro=>["#if 1\n", "#endif\n"]},
+ {name => "ANSWER", default=>["UV", 42]}, "NOTDEF",
+ {name => "Yes", type=>"YES"},
+ {name => "No", type=>"NO"},
+ {name => "Undef", type=>"UNDEF"},
+ # OK. It wasn't really designed to allow the creation of dual valued
+ # constants.
+ # It was more for INADDR_ANY INADDR_BROADCAST INADDR_LOOPBACK INADDR_NONE
+ {name=>"RFC1149", type=>"SV", value=>"sv_2mortal(temp_sv)",
+ pre=>"SV *temp_sv = newSVpv(RFC1149, 0); "
+ . "(void) SvUPGRADE(temp_sv,SVt_PVIV); SvIOK_on(temp_sv); "
+ . "SvIVX(temp_sv) = 1149;"},
+ );
+
+ push @items, $_ foreach keys %compass;
+
+ # Automatically compile the list of all the macro names, and make them
+ # exported constants.
+ my @export_names = map {(ref $_) ? $_->{name} : $_} @items;
+
+ # Exporter::Heavy (currently) isn't able to export the last 3 of these:
+ push @items, @common_items;
+
+ # XXX there are hardwired still.
+ my $test_body = <<'EOT';
# What follows goes to the temporary file.
# IV
my $five = FIVE;
if ($five == 5) {
print "ok 5\n";
} else {
- print "not ok 5 # $five\n";
+ print "not ok 5 # \$five\n";
}
# PV
@@ -354,7 +586,6 @@ unless (defined $undef) {
print "not ok 16 # \$undef='$undef'\n";
}
-
# invalid macro (chosen to look like a mix up between No and SW)
$notdef = eval { &ExtTest::So };
if (defined $notdef) {
@@ -379,10 +610,10 @@ my %compass = (
EOT
while (my ($point, $bearing) = each %compass) {
- print FH "'$point' => $bearing, "
+ $test_body .= "'$point' => $bearing, "
}
-print FH <<'EOT';
+$test_body .= <<'EOT';
);
@@ -408,7 +639,7 @@ if ($fail) {
EOT
-print FH <<"EOT";
+$test_body .= <<"EOT";
my \$rfc1149 = RFC1149;
if (\$rfc1149 ne "$parent_rfc1149") {
print "not ok 20 # '\$rfc1149' ne '$parent_rfc1149'\n";
@@ -424,7 +655,7 @@ if (\$rfc1149 != 1149) {
EOT
-print FH <<'EOT';
+$test_body .= <<'EOT';
# test macro=>1
my $open = OPEN;
if ($open eq '/*') {
@@ -433,8 +664,59 @@ if ($open eq '/*') {
print "not ok 22 # \$open='$open'\n";
}
EOT
+$dummytest+=18;
+
+ end_tests("Simple tests", \@items, \@export_names, $header, $test_body);
+}
if ($do_utf_tests) {
+ # utf8 tests
+ start_tests();
+ my ($inf, $pound_bytes, $pound_utf8);
+
+ $inf = chr 0x221E;
+ # Check that we can distiguish the pathological case of a string, and the
+ # utf8 representation of that string.
+ $pound_utf8 = $pound . '1';
+ if ($better_than_56) {
+ $pound_bytes = $pound_utf8;
+ utf8::encode ($pound_bytes);
+ } else {
+ # Must have that "U*" to generate a zero length UTF string that forces
+ # top bit set chars (such as the pound sign) into UTF8, so that the
+ # unpack 'C*' then gets the byte form of the UTF8.
+ $pound_bytes = pack 'C*', unpack 'C*', $pound_utf8 . pack "U*";
+ }
+
+ my @items = (@common_items,
+ {name=>$inf, type=>"PV", value=>'"Infinity"', macro=>1},
+ {name=>$pound_utf8, type=>"PV", value=>'"1 Pound"', macro=>1},
+ {name=>$pound_bytes, type=>"PV", value=>'"1 Pound (as bytes)"',
+ macro=>1},
+ );
+
+=pod
+
+The above set of names seems to produce a suitably bad set of compile
+problems on a Unicode naive version of ExtUtils::Constant (ie 0.11):
+
+nick@thinking-cap 15439-32-utf$ PERL_CORE=1 ./perl lib/ExtUtils/t/Constant.t
+1..33
+# perl=/stuff/perl5/15439-32-utf/perl
+# ext-30370 being created...
+Wide character in print at lib/ExtUtils/t/Constant.t line 140.
+ok 1
+ok 2
+# make = 'make'
+ExtTest.xs: In function `constant_1':
+ExtTest.xs:80: warning: multi-character character constant
+ExtTest.xs:80: warning: case value out of range
+ok 3
+
+=cut
+
+# Grr `
+
# Do this in 7 bit in case someone is testing with some settings that cause
# 8 bit files incapable of storing this character.
my @values
@@ -442,18 +724,20 @@ if ($do_utf_tests) {
($pound, $inf, $pound_bytes, $pound_utf8);
# Values is a list of strings, such as ('194,163,49', '163,49')
- print FH <<'EOT';
+ my $test_body .= "my \$test = $dummytest;\n";
+ $dummytest += 7 * 3; # 3 tests for each of the 7 things:
+
+ $test_body .= << 'EOT';
- # I can see that this child test program might be about to use parts of
- # Test::Builder
+use utf8;
+my $better_than_56 = $] > 5.007;
- my $test = 23;
- my ($pound, $inf, $pound_bytes, $pound_utf8) = map {eval "pack 'U*', $_"}
+my ($pound, $inf, $pound_bytes, $pound_utf8) = map {eval "pack 'U*', $_"}
EOT
- print FH join ",", @values;
+ $test_body .= join ",", @values;
- print FH << 'EOT';
+ $test_body .= << 'EOT';
;
foreach (["perl", "rules", "rules"],
@@ -479,9 +763,9 @@ foreach (["perl", "rules", "rules"],
}
EOT
- print FH "my (\$error, \$got) = ${package}::constant (\$string);\n";
+ $test_body .= "my (\$error, \$got) = ${package}::constant (\$string);\n";
- print FH <<'EOT';
+ $test_body .= <<'EOT';
if ($error or $got ne $expect) {
print "not ok $test # error '$error', got '$got'\n";
} else {
@@ -496,9 +780,9 @@ EOT
}
EOT
- print FH "my (\$error, \$got) = ${package}::constant (\$string);\n";
+ $test_body .= "my (\$error, \$got) = ${package}::constant (\$string);\n";
- print FH <<'EOT';
+ $test_body .= <<'EOT';
if ($error or $got ne $expect) {
print "not ok $test # error '$error', got '$got'\n";
} else {
@@ -515,9 +799,9 @@ EOT
}
EOT
- print FH "my (\$error, \$got) = ${package}::constant (\$string);\n";
+ $test_body .= "my (\$error, \$got) = ${package}::constant (\$string);\n";
- print FH <<'EOT';
+ $test_body .= <<'EOT';
if (ref $expect_bytes) {
# Error expected.
if ($error) {
@@ -534,229 +818,101 @@ EOT
}
}
EOT
-} else {
- # Don't utf tests;
- print FH <<'EOT';
-print "ok $_ # Skipped on non Unicode perl\n" foreach 23..43;
-EOT
-}
-
-close FH or die "close $testpl: $!\n";
-
-# This is where the test numbers carry on after the test number above are
-# relayed
-my $test = 44;
-################ Makefile.PL
-# We really need a Makefile.PL because make test for a no dynamic linking perl
-# will run Makefile.PL again as part of the "make perl" target.
-my $makefilePL = File::Spec->catdir($dir, "Makefile.PL");
-push @files, "Makefile.PL";
-open FH, ">$makefilePL" or die "open >$makefilePL: $!\n";
-print FH <<"EOT";
-#!$perl -w
-use ExtUtils::MakeMaker;
-WriteMakefile(
- 'NAME' => "$package",
- 'VERSION_FROM' => "$package.pm", # finds \$VERSION
- (\$] >= 5.005 ?
- (#ABSTRACT_FROM => "$package.pm", # XXX add this
- AUTHOR => "$0") : ())
- );
-EOT
-
-close FH or die "close $makefilePL: $!\n";
-
-################ MANIFEST
-# We really need a MANIFEST because make distclean checks it.
-my $manifest = File::Spec->catdir($dir, "MANIFEST");
-push @files, "MANIFEST";
-open FH, ">$manifest" or die "open >$manifest: $!\n";
-print FH "$_\n" foreach @files;
-close FH or die "close $manifest: $!\n";
-
-chdir $dir or die $!; push @INC, '../../lib';
-END {chdir ".." or warn $!};
-
-my $core = $ENV{PERL_CORE} ? ' PERL_CORE=1' : '';
-my @perlout = `$runperl Makefile.PL $core`;
-if ($?) {
- print "not ok 1 # $runperl Makefile.PL failed: $?\n";
- print "# $_" foreach @perlout;
- exit($?);
-} else {
- print "ok 1\n";
+ end_tests("utf8 tests", \@items, [], "#define perl \"rules\"\n", $test_body);
}
+# XXX I think that I should merge this into the utf8 test above.
+sub explict_call_constant {
+ my ($string, $expect) = @_;
+ # This does assume simple strings suitable for ''
+ my $test_body = <<"EOT";
+{
+ my (\$error, \$got) = ${package}::constant ('$string');\n;
+EOT
-my $makefile = ($^O eq 'VMS' ? 'descrip' : 'Makefile');
-my $makefile_ext = ($^O eq 'VMS' ? '.mms' : '');
-if (-f "$makefile$makefile_ext") {
- print "ok 2\n";
-} else {
- print "not ok 2\n";
-}
-
-# Renamed by make clean
-my $makefile_rename = $makefile . ($^O eq 'VMS' ? '.mms' : '.old');
-
-my $make = $Config{make};
-
-$make = $ENV{MAKE} if exists $ENV{MAKE};
-
-if ($^O eq 'MSWin32' && $make eq 'nmake') { $make .= " -nologo"; }
-
-my @makeout;
-
-if ($^O eq 'VMS') { $make .= ' all'; }
-print "# make = '$make'\n";
-@makeout = `$make`;
-if ($?) {
- print "not ok 3 # $make failed: $?\n";
- print "# $_" foreach @makeout;
- exit($?);
-} else {
- print "ok 3\n";
-}
-
-if ($^O eq 'VMS') { $make =~ s{ all}{}; }
-
-if ($Config{usedl}) {
- print "ok 4\n";
-} else {
- my $makeperl = "$make perl";
- print "# make = '$makeperl'\n";
- @makeout = `$makeperl`;
- if ($?) {
- print "not ok 4 # $makeperl failed: $?\n";
- print "# $_" foreach @makeout;
- exit($?);
+ if (defined $expect) {
+ # No error expected
+ $test_body .= <<"EOT";
+ if (\$error or \$got ne "$expect") {
+ print "not ok $dummytest # error '\$error', expect '$expect', got '\$got'\n";
} else {
- print "ok 4\n";
+ print "ok $dummytest\n";
+ }
}
+EOT
+ } else {
+ # Error expected.
+ $test_body .= <<"EOT";
+ if (\$error) {
+ print "ok $dummytest # error='\$error' (as expected)\n";
+ } else {
+ print "not ok $dummytest # expected error, got no error and '\$got'\n";
+ }
+EOT
+ }
+ $dummytest++;
+ return $test_body . <<'EOT';
}
-
-my $maketest = "$make test";
-print "# make = '$maketest'\n";
-
-@makeout = `$maketest`;
-
-if (open OUTPUT, "<$output") {
- print while <OUTPUT>;
- close OUTPUT or print "# Close $output failed: $!\n";
-} else {
- # Harness will report missing test results at this point.
- print "# Open <$output failed: $!\n";
+EOT
}
-if ($?) {
- print "not ok $test # $maketest failed: $?\n";
- print "# $_" foreach @makeout;
+# Simple tests to verify bits of the switch generation system work.
+sub simple {
+ start_tests();
+ # Deliberately leave $name in @_, so that it is indexed from 1.
+ my ($name, @items) = @_;
+ my $test_header;
+ my $test_body = "my \$value;\n";
+ foreach my $counter (1 .. $#_) {
+ my $thisname = $_[$counter];
+ $test_header .= "#define $thisname $counter\n";
+ $test_body .= <<"EOT";
+\$value = $thisname;
+if (\$value == $counter) {
+ print "ok $dummytest\n";
} else {
- print "ok $test - maketest\n";
+ print "not ok $dummytest # $thisname gave \$value\n";
}
-$test++;
-
-
-# -x is busted on Win32 < 5.6.1, so we emulate it.
-my $regen;
-if( $^O eq 'MSWin32' && $] <= 5.006001 ) {
- open(REGENTMP, ">regentmp") or die $!;
- open(XS, "$package.xs") or die $!;
- my $saw_shebang;
- while(<XS>) {
- $saw_shebang++ if /^#!.*/i ;
- print REGENTMP $_ if $saw_shebang;
+EOT
+ ++$dummytest;
+ # Yes, the last time round the loop appends a z to the string.
+ for my $i (0 .. length $thisname) {
+ my $copyname = $thisname;
+ substr ($copyname, $i, 1) = 'z';
+ $test_body .= explict_call_constant ($copyname,
+ $copyname eq $thisname
+ ? $thisname : undef);
}
- close XS; close REGENTMP;
- $regen = `$runperl regentmp`;
- unlink 'regentmp';
-}
-else {
- $regen = `$runperl -x $package.xs`;
-}
-if ($?) {
- print "not ok $test # $runperl -x $package.xs failed: $?\n";
-} else {
- print "ok $test - regen\n";
-}
-$test++;
-
-my $expect = $constant_types . $C_constant .
- "\n#### XS Section:\n" . $XS_constant;
-
-if ($expect eq $regen) {
- print "ok $test - regen worked\n";
-} else {
- print "not ok $test - regen worked\n";
- # open FOO, ">expect"; print FOO $expect;
- # open FOO, ">regen"; print FOO $regen; close FOO;
-}
-$test++;
-
-my $makeclean = "$make clean";
-print "# make = '$makeclean'\n";
-@makeout = `$makeclean`;
-if ($?) {
- print "not ok $test # $make failed: $?\n";
- print "# $_" foreach @makeout;
-} else {
- print "ok $test\n";
-}
-$test++;
-
-sub check_for_bonus_files {
- my $dir = shift;
- my %expect = map {($^O eq 'VMS' ? lc($_) : $_), 1} @_;
-
- my $fail;
- opendir DIR, $dir or die "opendir '$dir': $!";
- while (defined (my $entry = readdir DIR)) {
- $entry =~ s/\.$// if $^O eq 'VMS'; # delete trailing dot that indicates no extension
- next if $expect{$entry};
- print "# Extra file '$entry'\n";
- $fail = 1;
}
-
- closedir DIR or warn "closedir '.': $!";
- if ($fail) {
- print "not ok $test\n";
- } else {
- print "ok $test\n";
- }
- $test++;
+ # Ho. This seems to be buggy in 5.005_03:
+ # # Now remove $name from @_:
+ # shift @_;
+ end_tests($name, \@items, \@items, $test_header, $test_body);
}
-check_for_bonus_files ('.', @files, $output, $makefile_rename, '.', '..');
-
-rename $makefile_rename, $makefile
- or die "Can't rename '$makefile_rename' to '$makefile': $!";
-
-unlink $output or warn "Can't unlink '$output': $!";
+# Check that the memeq clauses work correctly when there isn't a switch
+# statement to bump off a character
+simple ("Singletons", "A", "AB", "ABC", "ABCD", "ABCDE");
+# Check the three code.
+simple ("Three start", qw(Bea kea Lea lea nea pea rea sea tea Wea yea Zea));
+# There were 162 2 letter words in /usr/share/dict/words on FreeBSD 4.6, which
+# I felt was rather too many. So I used words with 2 vowels.
+simple ("Twos and three middle", qw(aa ae ai ea eu ie io oe era eta));
+# Given the choice go for the end, else the earliest point
+simple ("Three end and four symetry", qw(ean ear eat barb marm tart));
-# Need to make distclean to remove ../../lib/ExtTest.pm
-my $makedistclean = "$make distclean";
-print "# make = '$makedistclean'\n";
-@makeout = `$makedistclean`;
-if ($?) {
- print "not ok $test # $make failed: $?\n";
- print "# $_" foreach @makeout;
-} else {
- print "ok $test\n";
-}
-$test++;
-
-check_for_bonus_files ('.', @files, '.', '..');
-unless ($keep_files) {
- foreach (@files) {
- unlink $_ or warn "unlink $_: $!";
- }
-}
+# Need this if the single test below is rolled into @tests :
+# --$dummytest;
+print "1..$dummytest\n";
-check_for_bonus_files ('.', '.', '..');
+write_and_run_extension @$_ foreach @tests;
# This was causing an assertion failure (a C<confess>ion)
+# Any single byte > 128 should do it.
C_constant ($package, undef, undef, undef, undef, undef, chr 255);
+print "ok $realtest\n"; $realtest++;
-print "ok $test\n"; $test++;
+print STDERR "# You were running with \$keep_files set to $keep_files\n"
+ if $keep_files;