| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # $Id: CheckLib.pm,v 1.25 2008/10/27 12:16:23 drhyde Exp $ | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | package Devel::CheckLib; | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 11 |  |  | 11 |  | 7942 | use 5.00405; #postfix foreach | 
|  | 11 |  |  |  |  | 35 |  | 
| 6 | 11 |  |  | 11 |  | 51 | use strict; | 
|  | 11 |  |  |  |  | 14 |  | 
|  | 11 |  |  |  |  | 267 |  | 
| 7 | 11 |  |  | 11 |  | 47 | use vars qw($VERSION @ISA @EXPORT); | 
|  | 11 |  |  |  |  | 18 |  | 
|  | 11 |  |  |  |  | 650 |  | 
| 8 |  |  |  |  |  |  | $VERSION = '1.15'; | 
| 9 | 11 |  |  | 11 |  | 55 | use Config qw(%Config); | 
|  | 11 |  |  |  |  | 23 |  | 
|  | 11 |  |  |  |  | 474 |  | 
| 10 | 11 |  |  | 11 |  | 4298 | use Text::ParseWords qw(quotewords shellwords); | 
|  | 11 |  |  |  |  | 12444 |  | 
|  | 11 |  |  |  |  | 575 |  | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 11 |  |  | 11 |  | 63 | use File::Spec; | 
|  | 11 |  |  |  |  | 23 |  | 
|  | 11 |  |  |  |  | 216 |  | 
| 13 | 11 |  |  | 11 |  | 2593 | use File::Temp; | 
|  | 11 |  |  |  |  | 58701 |  | 
|  | 11 |  |  |  |  | 1816 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  | require Exporter; | 
| 16 |  |  |  |  |  |  | @ISA = qw(Exporter); | 
| 17 |  |  |  |  |  |  | @EXPORT = qw(assert_lib check_lib_or_exit check_lib); | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | # localising prevents the warningness leaking out of this module | 
| 20 |  |  |  |  |  |  | local $^W = 1;    # use warnings is a 5.6-ism | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | _findcc(); # bomb out early if there's no compiler | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | =head1 NAME | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | Devel::CheckLib - check that a library is available | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | Devel::CheckLib is a perl module that checks whether a particular C | 
| 31 |  |  |  |  |  |  | library and its headers are available. | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | use Devel::CheckLib; | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | check_lib_or_exit( lib => 'jpeg', header => 'jpeglib.h' ); | 
| 38 |  |  |  |  |  |  | check_lib_or_exit( lib => [ 'iconv', 'jpeg' ] ); | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | # or prompt for path to library and then do this: | 
| 41 |  |  |  |  |  |  | check_lib_or_exit( lib => 'jpeg', libpath => $additional_path ); | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | =head1 USING IT IN Makefile.PL or Build.PL | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | If you want to use this from Makefile.PL or Build.PL, do | 
| 46 |  |  |  |  |  |  | not simply copy the module into your distribution as this may cause | 
| 47 |  |  |  |  |  |  | problems when PAUSE and search.cpan.org index the distro.  Instead, use | 
| 48 |  |  |  |  |  |  | the use-devel-checklib script. | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | =head1 HOW IT WORKS | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | You pass named parameters to a function, describing to it how to build | 
| 53 |  |  |  |  |  |  | and link to the libraries. | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | It works by trying to compile some code - which defaults to this: | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | int main(int argc, char *argv[]) { return 0; } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | and linking it to the specified libraries.  If something pops out the end | 
| 60 |  |  |  |  |  |  | which looks executable, it gets executed, and if main() returns 0 we know | 
| 61 |  |  |  |  |  |  | that it worked.  That tiny program is | 
| 62 |  |  |  |  |  |  | built once for each library that you specify, and (without linking) once | 
| 63 |  |  |  |  |  |  | for each header file. | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | If you want to check for the presence of particular functions in a | 
| 66 |  |  |  |  |  |  | library, or even that those functions return particular results, then | 
| 67 |  |  |  |  |  |  | you can pass your own function body for main() thus: | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | check_lib_or_exit( | 
| 70 |  |  |  |  |  |  | function => 'foo();if(libversion() > 5) return 0; else return 1;' | 
| 71 |  |  |  |  |  |  | incpath  => ... | 
| 72 |  |  |  |  |  |  | libpath  => ... | 
| 73 |  |  |  |  |  |  | lib      => ... | 
| 74 |  |  |  |  |  |  | header   => ... | 
| 75 |  |  |  |  |  |  | ); | 
| 76 |  |  |  |  |  |  |  | 
| 77 |  |  |  |  |  |  | In that case, it will fail to build if either foo() or libversion() don't | 
| 78 |  |  |  |  |  |  | exist, and main() will return the wrong value if libversion()'s return | 
| 79 |  |  |  |  |  |  | value isn't what you want. | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | =head1 FUNCTIONS | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  | All of these take the same named parameters and are exported by default. | 
| 84 |  |  |  |  |  |  | To avoid exporting them, C | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | =head2 assert_lib | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | This takes several named parameters, all of which are optional, and dies | 
| 89 |  |  |  |  |  |  | with an error message if any of the libraries listed can | 
| 90 |  |  |  |  |  |  | not be found.  B: dying in a Makefile.PL or Build.PL may provoke | 
| 91 |  |  |  |  |  |  | a 'FAIL' report from CPAN Testers' automated smoke testers.  Use | 
| 92 |  |  |  |  |  |  | C instead. | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | The named parameters are: | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | =over | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | =item lib | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | Must be either a string with the name of a single | 
| 101 |  |  |  |  |  |  | library or a reference to an array of strings of library names.  Depending | 
| 102 |  |  |  |  |  |  | on the compiler found, library names will be fed to the compiler either as | 
| 103 |  |  |  |  |  |  | C<-l> arguments or as C<.lib> file names.  (E.g. C<-ljpeg> or C) | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | =item libpath | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | a string or an array of strings | 
| 108 |  |  |  |  |  |  | representing additional paths to search for libraries. | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | =item LIBS | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | a C-style space-separated list of | 
| 113 |  |  |  |  |  |  | libraries (each preceded by '-l') and directories (preceded by '-L'). | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | This can also be supplied on the command-line. | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =item debug | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | If true - emit information during processing that can be used for | 
| 120 |  |  |  |  |  |  | debugging. | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | =back | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | And libraries are no use without header files, so ... | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | =over | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | =item header | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | Must be either a string with the name of a single | 
| 131 |  |  |  |  |  |  | header file or a reference to an array of strings of header file names. | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | =item incpath | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | a string or an array of strings | 
| 136 |  |  |  |  |  |  | representing additional paths to search for headers. | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | =item INC | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | a C-style space-separated list of | 
| 141 |  |  |  |  |  |  | incpaths, each preceded by '-I'. | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | This can also be supplied on the command-line. | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | =item ccflags | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | Extra flags to pass to the compiler. | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | =item ldflags | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | Extra flags to pass to the linker. | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | =item analyze_binary | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | a callback function that will be invoked in order to perform custom | 
| 156 |  |  |  |  |  |  | analysis of the generated binary. The callback arguments are the | 
| 157 |  |  |  |  |  |  | library name and the path to the binary just compiled. | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | It is possible to use this callback, for instance, to inspect the | 
| 160 |  |  |  |  |  |  | binary for further dependencies. | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =item not_execute | 
| 163 |  |  |  |  |  |  |  | 
| 164 |  |  |  |  |  |  | Do not try to execute generated binary. Only check that compilation has not failed. | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | =back | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | =head2 check_lib_or_exit | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | This behaves exactly the same as C except that instead of | 
| 171 |  |  |  |  |  |  | dieing, it warns (with exactly the same error message) and exits. | 
| 172 |  |  |  |  |  |  | This is intended for use in Makefile.PL / Build.PL | 
| 173 |  |  |  |  |  |  | when you might want to prompt the user for various paths and | 
| 174 |  |  |  |  |  |  | things before checking that what they've told you is sane. | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | If any library or header is missing, it exits with an exit value of 0 to avoid | 
| 177 |  |  |  |  |  |  | causing a CPAN Testers 'FAIL' report.  CPAN Testers should ignore this | 
| 178 |  |  |  |  |  |  | result -- which is what you want if an external library dependency is not | 
| 179 |  |  |  |  |  |  | available. | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | =head2 check_lib | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | This behaves exactly the same as C except that it is silent, | 
| 184 |  |  |  |  |  |  | returning false instead of dieing, or true otherwise. | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | =cut | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | sub check_lib_or_exit { | 
| 189 | 0 |  |  | 0 | 1 | 0 | eval 'assert_lib(@_)'; | 
| 190 | 0 | 0 |  |  |  | 0 | if($@) { | 
| 191 | 0 |  |  |  |  | 0 | warn $@; | 
| 192 | 0 |  |  |  |  | 0 | exit; | 
| 193 |  |  |  |  |  |  | } | 
| 194 |  |  |  |  |  |  | } | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | sub check_lib { | 
| 197 | 8 |  |  | 8 | 1 | 19074 | eval 'assert_lib(@_)'; | 
| 198 | 8 | 100 |  |  |  | 359 | return $@ ? 0 : 1; | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | # borrowed from Text::ParseWords | 
| 202 |  |  |  |  |  |  | sub _parse_line { | 
| 203 | 31 |  |  | 31 |  | 155 | my($delimiter, $keep, $line) = @_; | 
| 204 | 31 |  |  |  |  | 114 | my($word, @pieces); | 
| 205 |  |  |  |  |  |  |  | 
| 206 | 11 |  |  | 11 |  | 70 | no warnings 'uninitialized';  # we will be testing undef strings | 
|  | 11 |  |  |  |  | 32 |  | 
|  | 11 |  |  |  |  | 26188 |  | 
| 207 |  |  |  |  |  |  |  | 
| 208 | 31 |  |  |  |  | 137 | while (length($line)) { | 
| 209 |  |  |  |  |  |  | # This pattern is optimised to be stack conservative on older perls. | 
| 210 |  |  |  |  |  |  | # Do not refactor without being careful and testing it on very long strings. | 
| 211 |  |  |  |  |  |  | # See Perl bug #42980 for an example of a stack busting input. | 
| 212 | 0 | 0 |  |  |  | 0 | $line =~ s/^ | 
| 213 |  |  |  |  |  |  | (?: | 
| 214 |  |  |  |  |  |  | # double quoted string | 
| 215 |  |  |  |  |  |  | (")                             # $quote | 
| 216 |  |  |  |  |  |  | ((?>[^\\"]*(?:\\.[^\\"]*)*))"   # $quoted | 
| 217 |  |  |  |  |  |  | | # --OR-- | 
| 218 |  |  |  |  |  |  | # singe quoted string | 
| 219 |  |  |  |  |  |  | (')                             # $quote | 
| 220 |  |  |  |  |  |  | ((?>[^\\']*(?:\\.[^\\']*)*))'   # $quoted | 
| 221 |  |  |  |  |  |  | |   # --OR-- | 
| 222 |  |  |  |  |  |  | # unquoted string | 
| 223 |  |  |  |  |  |  | (                               # $unquoted | 
| 224 |  |  |  |  |  |  | (?:\\.|[^\\"'])*? | 
| 225 |  |  |  |  |  |  | ) | 
| 226 |  |  |  |  |  |  | # followed by | 
| 227 |  |  |  |  |  |  | (                               # $delim | 
| 228 |  |  |  |  |  |  | \Z(?!\n)                    # EOL | 
| 229 |  |  |  |  |  |  | |   # --OR-- | 
| 230 |  |  |  |  |  |  | (?-x:$delimiter)            # delimiter | 
| 231 |  |  |  |  |  |  | |   # --OR-- | 
| 232 |  |  |  |  |  |  | (?!^)(?=["'])               # a quote | 
| 233 |  |  |  |  |  |  | ) | 
| 234 |  |  |  |  |  |  | )//xs or return;    # extended layout | 
| 235 | 0 | 0 |  |  |  | 0 | my ($quote, $quoted, $unquoted, $delim) = (($1 ? ($1,$2) : ($3,$4)), $5, $6); | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 0 | 0 | 0 |  |  | 0 | return() unless( defined($quote) || length($unquoted) || length($delim)); | 
|  |  |  | 0 |  |  |  |  | 
| 238 |  |  |  |  |  |  |  | 
| 239 | 0 | 0 |  |  |  | 0 | if ($keep) { | 
| 240 | 0 |  |  |  |  | 0 | $quoted = "$quote$quoted$quote"; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  | else { | 
| 243 | 0 |  |  |  |  | 0 | $unquoted =~ s/\\(.)/$1/sg; | 
| 244 | 0 | 0 |  |  |  | 0 | if (defined $quote) { | 
| 245 | 0 | 0 |  |  |  | 0 | $quoted =~ s/\\(.)/$1/sg if ($quote eq '"'); | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  | } | 
| 248 | 0 |  |  |  |  | 0 | $word .= substr($line, 0, 0); # leave results tainted | 
| 249 | 0 | 0 |  |  |  | 0 | $word .= defined $quote ? $quoted : $unquoted; | 
| 250 |  |  |  |  |  |  |  | 
| 251 | 0 | 0 |  |  |  | 0 | if (length($delim)) { | 
| 252 | 0 |  |  |  |  | 0 | push(@pieces, $word); | 
| 253 | 0 | 0 |  |  |  | 0 | push(@pieces, $delim) if ($keep eq 'delimiters'); | 
| 254 | 0 |  |  |  |  | 0 | undef $word; | 
| 255 |  |  |  |  |  |  | } | 
| 256 | 0 | 0 |  |  |  | 0 | if (!length($line)) { | 
| 257 | 0 |  |  |  |  | 0 | push(@pieces, $word); | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  | } | 
| 260 | 31 |  |  |  |  | 96 | return(@pieces); | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | sub _parsewords { | 
| 264 | 126 | 50 |  | 126 |  | 963 | return shellwords @_ if $^O ne 'MSWin32'; | 
| 265 |  |  |  |  |  |  | # for Win32, take off "" but leave \ | 
| 266 | 0 |  | 0 |  |  | 0 | map { my $s=$_; $s =~ s/^"(.*)"$/$1/; $s } grep defined && length, quotewords '\s+', 1, @_; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | sub _compile_cmd { | 
| 270 | 48 |  |  | 48 |  | 930 | my ($Config_cc, $cc, $cfile, $exefile, $incpaths, $ld, $Config_libs, $lib, $libpaths) = @_; | 
| 271 | 48 |  |  |  |  | 219 | my @sys_cmd = @$cc; | 
| 272 | 48 | 50 |  |  |  | 242 | if ( $Config_cc eq 'cl' ) {                 # Microsoft compiler | 
|  |  | 50 |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | # this is horribly sensitive to the order of arguments | 
| 274 | 0 | 0 |  |  |  | 0 | push @sys_cmd, | 
|  |  | 0 |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | $cfile, | 
| 276 |  |  |  |  |  |  | (defined $lib ? "${lib}.lib" : ()), | 
| 277 |  |  |  |  |  |  | "/Fe$exefile", | 
| 278 |  |  |  |  |  |  | (map '/I'.$_, @$incpaths), | 
| 279 |  |  |  |  |  |  | "/link", | 
| 280 |  |  |  |  |  |  | @$ld, | 
| 281 |  |  |  |  |  |  | _parsewords($Config_libs), | 
| 282 |  |  |  |  |  |  | (defined $lib ? map '/libpath:'.$_, @$libpaths : ()), | 
| 283 |  |  |  |  |  |  | ; | 
| 284 |  |  |  |  |  |  | } elsif($Config_cc =~ /bcc32(\.exe)?/) {    # Borland | 
| 285 | 0 | 0 |  |  |  | 0 | push @sys_cmd, | 
| 286 |  |  |  |  |  |  | @$ld, | 
| 287 |  |  |  |  |  |  | (map "-I$_", @$incpaths), | 
| 288 |  |  |  |  |  |  | "-o$exefile", | 
| 289 |  |  |  |  |  |  | (defined $lib ? ((map "-L$_", @$libpaths), "-l$lib") : ()), | 
| 290 |  |  |  |  |  |  | $cfile, | 
| 291 |  |  |  |  |  |  | ; | 
| 292 |  |  |  |  |  |  | } else { # Unix-ish: gcc, Sun, AIX (gcc, cc), ... | 
| 293 |  |  |  |  |  |  | push @sys_cmd, | 
| 294 |  |  |  |  |  |  | (map "-I$_", @$incpaths), | 
| 295 |  |  |  |  |  |  | $cfile, | 
| 296 |  |  |  |  |  |  | (!defined $lib ? () : ( | 
| 297 |  |  |  |  |  |  | (map "-L$_", @$libpaths), | 
| 298 | 48 | 50 |  |  |  | 535 | ($^O eq 'darwin' ? (map { "-Wl,-rpath,$_" } @$libpaths) : ()), | 
|  | 0 | 100 |  |  |  | 0 |  | 
| 299 |  |  |  |  |  |  | "-l$lib", | 
| 300 |  |  |  |  |  |  | )), | 
| 301 |  |  |  |  |  |  | @$ld, | 
| 302 |  |  |  |  |  |  | "-o", $exefile, | 
| 303 |  |  |  |  |  |  | ; | 
| 304 |  |  |  |  |  |  | } | 
| 305 | 48 |  |  |  |  | 252 | @sys_cmd; | 
| 306 |  |  |  |  |  |  | } | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | sub _make_cfile { | 
| 309 | 43 |  |  | 43 |  | 184 | my ($use_headers, $function, $debug) = @_; | 
| 310 | 43 |  |  |  |  | 158 | my $code = ''; | 
| 311 | 43 |  |  |  |  | 222 | $code .= qq{#include <$_>\n} for @$use_headers; | 
| 312 | 43 |  | 100 |  |  | 341 | $code .= "int main(int argc, char *argv[]) { ".($function || 'return 0;')." }\n"; | 
| 313 | 43 | 50 |  |  |  | 146 | if ($debug) { | 
| 314 | 0 |  |  |  |  | 0 | (my $c = $code) =~ s:^:# :gm; | 
| 315 | 0 |  |  |  |  | 0 | warn "# Code:\n$c\n"; | 
| 316 |  |  |  |  |  |  | } | 
| 317 | 43 |  |  |  |  | 564 | my ($ch, $cfile) = File::Temp::tempfile( | 
| 318 |  |  |  |  |  |  | 'assertlibXXXXXXXX', SUFFIX => '.c' | 
| 319 |  |  |  |  |  |  | ); | 
| 320 | 43 |  |  |  |  | 19957 | print $ch $code; | 
| 321 | 43 |  |  |  |  | 1939 | close $ch; | 
| 322 | 43 |  |  |  |  | 823 | (my $ofile = $cfile) =~ s/\.c$/$Config{_o}/; | 
| 323 | 43 |  |  |  |  | 353 | ($cfile, $ofile); | 
| 324 |  |  |  |  |  |  | } | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | sub assert_lib { | 
| 327 | 31 |  |  | 31 | 1 | 67749 | my %args = @_; | 
| 328 |  |  |  |  |  |  | $args{$_} = [$args{$_}] | 
| 329 | 31 |  | 100 |  |  | 656 | for grep $args{$_} && !ref($args{$_}), qw(lib libpath header incpath); | 
| 330 | 31 | 100 |  |  |  | 90 | my @libs = @{$args{lib} || []}; | 
|  | 31 |  |  |  |  | 267 |  | 
| 331 | 31 | 100 |  |  |  | 97 | my @libpaths = @{$args{libpath} || []}; | 
|  | 31 |  |  |  |  | 200 |  | 
| 332 | 31 | 100 |  |  |  | 78 | my @headers = @{$args{header} || []}; | 
|  | 31 |  |  |  |  | 158 |  | 
| 333 | 31 | 100 |  |  |  | 79 | my @incpaths = @{$args{incpath} || []}; | 
|  | 31 |  |  |  |  | 188 |  | 
| 334 | 31 |  |  |  |  | 82 | my $analyze_binary = $args{analyze_binary}; | 
| 335 | 31 |  |  |  |  | 74 | my $execute = !$args{not_execute}; | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 31 |  |  |  |  | 73 | my @argv = @ARGV; | 
| 338 | 31 |  | 50 |  |  | 383 | push @argv, _parse_line('\s+', 0, $ENV{PERL_MM_OPT}||''); | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | # work-a-like for Makefile.PL's LIBS and INC arguments | 
| 341 |  |  |  |  |  |  | # if given as command-line argument, append to %args | 
| 342 | 31 |  |  |  |  | 107 | for my $arg (@argv) { | 
| 343 | 0 |  |  |  |  | 0 | for my $mm_attr_key (qw(LIBS INC)) { | 
| 344 | 0 | 0 |  |  |  | 0 | if (my ($mm_attr_value) = $arg =~ /\A $mm_attr_key = (.*)/x) { | 
| 345 |  |  |  |  |  |  | # it is tempting to put some \s* into the expression, but the | 
| 346 |  |  |  |  |  |  | # MM command-line parser only accepts LIBS etc. followed by =, | 
| 347 |  |  |  |  |  |  | # so we should not be any more lenient with whitespace than that | 
| 348 | 0 |  |  |  |  | 0 | $args{$mm_attr_key} .= " $mm_attr_value"; | 
| 349 |  |  |  |  |  |  | } | 
| 350 |  |  |  |  |  |  | } | 
| 351 |  |  |  |  |  |  | } | 
| 352 |  |  |  |  |  |  |  | 
| 353 | 31 | 100 |  |  |  | 101 | if(defined($args{LIBS})) { | 
| 354 | 1 |  |  |  |  | 7 | foreach my $arg (_parsewords($args{LIBS})) { | 
| 355 | 3 | 50 |  |  |  | 195 | die("LIBS argument badly-formed: $arg\n") unless($arg =~ /^-[lLR]/); | 
| 356 | 3 | 100 |  |  |  | 5 | push @{$arg =~ /^-l/ ? \@libs : \@libpaths}, substr($arg, 2); | 
|  | 3 |  |  |  |  | 14 |  | 
| 357 |  |  |  |  |  |  | } | 
| 358 |  |  |  |  |  |  | } | 
| 359 | 31 | 100 |  |  |  | 91 | if(defined($args{INC})) { | 
| 360 | 2 |  |  |  |  | 8 | foreach my $arg (_parsewords($args{INC})) { | 
| 361 | 3 | 100 |  |  |  | 180 | die("INC argument badly-formed: $arg\n") unless($arg =~ /^-I/); | 
| 362 | 2 |  |  |  |  | 12 | push @incpaths, substr($arg, 2); | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  | } | 
| 365 |  |  |  |  |  |  |  | 
| 366 | 30 |  |  |  |  | 271 | my ($cc, $ld) = _findcc($args{debug}, $args{ccflags}, $args{ldflags}); | 
| 367 | 30 |  |  |  |  | 161 | my @missing; | 
| 368 |  |  |  |  |  |  | my @wrongresult; | 
| 369 | 30 |  |  |  |  | 0 | my @wronganalysis; | 
| 370 | 30 |  |  |  |  | 0 | my @use_headers; | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | # first figure out which headers we can't find ... | 
| 373 | 30 |  |  |  |  | 72 | for my $header (@headers) { | 
| 374 | 13 |  |  |  |  | 27 | push @use_headers, $header; | 
| 375 | 13 |  |  |  |  | 65 | my ($cfile, $ofile) = _make_cfile(\@use_headers, '', $args{debug}); | 
| 376 | 13 |  |  |  |  | 142 | my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; | 
| 377 | 13 |  |  |  |  | 1934 | my @sys_cmd = _compile_cmd($Config{cc}, $cc, $cfile, $exefile, \@incpaths, $ld, $Config{libs}); | 
| 378 | 13 | 50 |  |  |  | 55 | warn "# @sys_cmd\n" if $args{debug}; | 
| 379 | 13 | 50 |  |  |  | 66 | my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); | 
| 380 | 13 | 100 | 66 |  |  | 658 | push @missing, $header if $rv != 0 || ! -f $exefile; | 
| 381 | 13 |  |  |  |  | 205 | _cleanup_exe($exefile); | 
| 382 | 13 |  |  |  |  | 656 | unlink $cfile; | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | # now do each library in turn with headers | 
| 386 | 30 |  |  |  |  | 313 | my ($cfile, $ofile) = _make_cfile(\@use_headers, @args{qw(function debug)}); | 
| 387 | 30 |  |  |  |  | 127 | for my $lib ( @libs ) { | 
| 388 | 35 | 50 |  |  |  | 305 | last if $Config{cc} eq 'CC/DECC';          # VMS | 
| 389 | 35 |  |  |  |  | 481 | my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; | 
| 390 | 35 |  |  |  |  | 7810 | my @sys_cmd = _compile_cmd($Config{cc}, $cc, $cfile, $exefile, \@incpaths, $ld, $Config{libs}, $lib, \@libpaths); | 
| 391 | 35 | 50 |  |  |  | 222 | warn "# @sys_cmd\n" if $args{debug}; | 
| 392 | 35 | 50 | 33 |  |  | 843 | local $ENV{LD_RUN_PATH} = join(":", grep $_, @libpaths, $ENV{LD_RUN_PATH}) unless $^O eq 'MSWin32' or $^O eq 'darwin'; | 
| 393 | 35 | 50 |  |  |  | 152 | local $ENV{PATH} = join(";", @libpaths).";".$ENV{PATH} if $^O eq 'MSWin32'; | 
| 394 | 35 | 50 |  |  |  | 208 | my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); | 
| 395 | 35 | 100 | 66 |  |  | 1211 | if ($rv != 0 || ! -f $exefile) { | 
| 396 | 11 |  |  |  |  | 115 | push @missing, $lib; | 
| 397 |  |  |  |  |  |  | } | 
| 398 |  |  |  |  |  |  | else { | 
| 399 | 24 |  |  |  |  | 562 | chmod 0755, $exefile; | 
| 400 | 24 |  |  |  |  | 3256 | my $absexefile = File::Spec->rel2abs($exefile); | 
| 401 | 24 | 50 |  |  |  | 445 | $absexefile = '"'.$absexefile.'"' if $absexefile =~ m/\s/; | 
| 402 | 24 | 50 |  |  |  | 198 | warn "# Execute($execute): $absexefile\n" if $args{debug}; | 
| 403 | 24 | 50 |  |  |  | 123 | if ($execute) { | 
| 404 | 24 |  |  |  |  | 70208 | my $retval = system($absexefile); | 
| 405 | 24 | 50 |  |  |  | 536 | warn "# return value: $retval\n" if $args{debug}; | 
| 406 | 24 | 100 |  |  |  | 392 | push @wrongresult, $lib if $retval != 0; | 
| 407 |  |  |  |  |  |  | } | 
| 408 | 24 | 100 | 100 |  |  | 354 | push @wronganalysis, $lib | 
| 409 |  |  |  |  |  |  | if $analyze_binary and !$analyze_binary->($lib, $exefile); | 
| 410 |  |  |  |  |  |  | } | 
| 411 | 35 |  |  |  |  | 8677 | _cleanup_exe($exefile); | 
| 412 |  |  |  |  |  |  | } | 
| 413 | 30 |  |  |  |  | 1325 | unlink $cfile; | 
| 414 |  |  |  |  |  |  |  | 
| 415 | 30 |  |  |  |  | 307 | my $miss_string = join( q{, }, map qq{'$_'}, @missing ); | 
| 416 | 30 | 100 |  |  |  | 842 | die("Can't link/include C library $miss_string, aborting.\n") if @missing; | 
| 417 | 20 |  |  |  |  | 164 | my $wrong_string = join( q{, }, map qq{'$_'}, @wrongresult); | 
| 418 | 20 | 100 |  |  |  | 283 | die("wrong result: $wrong_string\n") if @wrongresult; | 
| 419 | 18 |  |  |  |  | 115 | my $analysis_string = join(q{, }, map qq{'$_'}, @wronganalysis ); | 
| 420 | 18 | 100 |  |  |  | 1351 | die("wrong analysis: $analysis_string") if @wronganalysis; | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | sub _cleanup_exe { | 
| 424 | 48 |  |  | 48 |  | 428 | my ($exefile) = @_; | 
| 425 | 48 |  |  |  |  | 365 | my $ofile = $exefile; | 
| 426 | 48 |  |  |  |  | 5093 | $ofile =~ s/$Config{_exe}$/$Config{_o}/; | 
| 427 |  |  |  |  |  |  | # List of files to remove | 
| 428 | 48 |  |  |  |  | 289 | my @rmfiles; | 
| 429 | 48 |  |  |  |  | 346 | push @rmfiles, $exefile, $ofile, "$exefile\.manifest"; | 
| 430 | 48 | 50 |  |  |  | 660 | if ( $Config{cc} eq 'cl' ) { | 
| 431 |  |  |  |  |  |  | # MSVC also creates foo.ilk and foo.pdb | 
| 432 | 0 |  |  |  |  | 0 | my $ilkfile = $exefile; | 
| 433 | 0 |  |  |  |  | 0 | $ilkfile =~ s/$Config{_exe}$/.ilk/; | 
| 434 | 0 |  |  |  |  | 0 | my $pdbfile = $exefile; | 
| 435 | 0 |  |  |  |  | 0 | $pdbfile =~ s/$Config{_exe}$/.pdb/; | 
| 436 | 0 |  |  |  |  | 0 | push @rmfiles, $ilkfile, $pdbfile; | 
| 437 |  |  |  |  |  |  | } | 
| 438 | 48 |  |  |  |  | 2893 | foreach (grep -f, @rmfiles) { | 
| 439 | 36 | 50 |  |  |  | 2594 | unlink $_ or warn "Could not remove $_: $!"; | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  | return | 
| 442 | 48 |  |  |  |  | 1342 | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | # return ($cc, $ld) | 
| 445 |  |  |  |  |  |  | # where $cc is an array ref of compiler name, compiler flags | 
| 446 |  |  |  |  |  |  | # where $ld is an array ref of linker flags | 
| 447 |  |  |  |  |  |  | sub _findcc { | 
| 448 | 41 |  |  | 41 |  | 236 | my ($debug, $user_ccflags, $user_ldflags) = @_; | 
| 449 |  |  |  |  |  |  | # Need to use $keep=1 to work with MSWin32 backslashes and quotes | 
| 450 | 41 |  |  |  |  | 1031 | my $Config_ccflags =  $Config{ccflags};  # use copy so ASPerl will compile | 
| 451 | 41 |  |  |  |  | 258 | $Config_ccflags =~ s:-O\S*::; # stop GCC optimising away test code | 
| 452 | 41 |  |  |  |  | 111 | my @Config_ldflags = (); | 
| 453 | 41 |  |  |  |  | 252 | for my $config_val ( @Config{qw(ldflags)} ){ | 
| 454 | 41 | 50 |  |  |  | 982 | push @Config_ldflags, $config_val if ( $config_val =~ /\S/ ); | 
| 455 |  |  |  |  |  |  | } | 
| 456 | 41 |  | 50 |  |  | 637 | my @ccflags = grep { length } _parsewords($Config_ccflags||'', $user_ccflags||''); | 
|  | 288 |  | 100 |  |  | 11527 |  | 
| 457 | 41 | 50 | 50 |  |  | 288 | my @ldflags = grep { length && $_ !~ m/^-Wl/ } _parsewords(@Config_ldflags, $user_ldflags||''); | 
|  | 82 |  |  |  |  | 3689 |  | 
| 458 | 41 |  |  |  |  | 761 | my @paths = split(/$Config{path_sep}/, $ENV{PATH}); | 
| 459 | 41 |  |  |  |  | 194 | my @cc = _parsewords($Config{cc}); | 
| 460 | 41 | 100 |  |  |  | 1958 | if (check_compiler ($cc[0], $debug)) { | 
| 461 | 1 |  |  |  |  | 5 | return ( [ @cc, @ccflags ], \@ldflags ); | 
| 462 |  |  |  |  |  |  | } | 
| 463 |  |  |  |  |  |  | # Find the extension for executables. | 
| 464 | 40 |  |  |  |  | 466 | my $exe = $Config{_exe}; | 
| 465 | 40 | 50 |  |  |  | 176 | if ($^O eq 'cygwin') { | 
| 466 | 0 |  |  |  |  | 0 | $exe = ''; | 
| 467 |  |  |  |  |  |  | } | 
| 468 | 40 |  |  |  |  | 112 | foreach my $path (@paths) { | 
| 469 |  |  |  |  |  |  | # Look for "$path/$cc[0].exe" | 
| 470 | 243 |  |  |  |  | 1872 | my $compiler = File::Spec->catfile($path, $cc[0]) . $exe; | 
| 471 | 243 | 100 |  |  |  | 536 | if (check_compiler ($compiler, $debug)) { | 
| 472 | 39 |  |  |  |  | 345 | return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags) | 
| 473 |  |  |  |  |  |  | } | 
| 474 | 204 | 50 |  |  |  | 469 | next if ! $exe; | 
| 475 |  |  |  |  |  |  | # Look for "$path/$cc[0]" without the .exe, if necessary. | 
| 476 | 0 |  |  |  |  | 0 | $compiler = File::Spec->catfile($path, $cc[0]); | 
| 477 | 0 | 0 |  |  |  | 0 | if (check_compiler ($compiler, $debug)) { | 
| 478 | 0 |  |  |  |  | 0 | return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags) | 
| 479 |  |  |  |  |  |  | } | 
| 480 |  |  |  |  |  |  | } | 
| 481 | 1 |  |  |  |  | 24 | die("Couldn't find your C compiler.\n"); | 
| 482 |  |  |  |  |  |  | } | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | sub check_compiler | 
| 485 |  |  |  |  |  |  | { | 
| 486 | 284 |  |  | 284 | 0 | 465 | my ($compiler, $debug) = @_; | 
| 487 | 284 | 100 | 66 |  |  | 3801 | if (-f $compiler && -x $compiler) { | 
| 488 | 40 | 50 |  |  |  | 129 | warn "# Compiler seems to be $compiler\n" if $debug; | 
| 489 | 40 |  |  |  |  | 124 | return 1; | 
| 490 |  |  |  |  |  |  | } | 
| 491 | 244 | 50 |  |  |  | 531 | warn "# Compiler was not $compiler\n" if $debug; | 
| 492 | 244 |  |  |  |  | 605 | return ''; | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | # code substantially borrowed from IPC::Run3 | 
| 497 |  |  |  |  |  |  | sub _quiet_system { | 
| 498 | 66 |  |  | 66 |  | 53748 | my (@cmd) = @_; | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | # save handles | 
| 501 | 66 |  |  |  |  | 340 | local *STDOUT_SAVE; | 
| 502 | 66 |  |  |  |  | 237 | local *STDERR_SAVE; | 
| 503 | 66 | 50 |  |  |  | 1982 | open STDOUT_SAVE, ">&STDOUT" or die "CheckLib: $! saving STDOUT"; | 
| 504 | 66 | 50 |  |  |  | 1466 | open STDERR_SAVE, ">&STDERR" or die "CheckLib: $! saving STDERR"; | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | # redirect to nowhere | 
| 507 | 66 |  |  |  |  | 226 | local *DEV_NULL; | 
| 508 | 66 | 50 |  |  |  | 3350 | open DEV_NULL, ">" . File::Spec->devnull | 
| 509 |  |  |  |  |  |  | or die "CheckLib: $! opening handle to null device"; | 
| 510 | 66 | 50 |  |  |  | 1796 | open STDOUT, ">&" . fileno DEV_NULL | 
| 511 |  |  |  |  |  |  | or die "CheckLib: $! redirecting STDOUT to null handle"; | 
| 512 | 66 | 50 |  |  |  | 1527 | open STDERR, ">&" . fileno DEV_NULL | 
| 513 |  |  |  |  |  |  | or die "CheckLib: $! redirecting STDERR to null handle"; | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | # run system command | 
| 516 | 66 |  |  |  |  | 2389228 | my $rv = system(@cmd); | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | # restore handles | 
| 519 | 66 | 50 |  |  |  | 6144 | open STDOUT, ">&" . fileno STDOUT_SAVE | 
| 520 |  |  |  |  |  |  | or die "CheckLib: $! restoring STDOUT handle"; | 
| 521 | 66 | 50 |  |  |  | 2211 | open STDERR, ">&" . fileno STDERR_SAVE | 
| 522 |  |  |  |  |  |  | or die "CheckLib: $! restoring STDERR handle"; | 
| 523 |  |  |  |  |  |  |  | 
| 524 | 66 |  |  |  |  | 5617 | return $rv; | 
| 525 |  |  |  |  |  |  | } | 
| 526 |  |  |  |  |  |  |  | 
| 527 |  |  |  |  |  |  | =head1 PLATFORMS SUPPORTED | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | You must have a C compiler installed.  We check for C<$Config{cc}>, | 
| 530 |  |  |  |  |  |  | both literally as it is in Config.pm and also in the $PATH. | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | It has been tested with varying degrees of rigorousness on: | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | =over | 
| 535 |  |  |  |  |  |  |  | 
| 536 |  |  |  |  |  |  | =item gcc (on Linux, *BSD, Mac OS X, Solaris, Cygwin) | 
| 537 |  |  |  |  |  |  |  | 
| 538 |  |  |  |  |  |  | =item Sun's compiler tools on Solaris | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | =item IBM's tools on AIX | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | =item SGI's tools on Irix 6.5 | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | =item Microsoft's tools on Windows | 
| 545 |  |  |  |  |  |  |  | 
| 546 |  |  |  |  |  |  | =item MinGW on Windows (with Strawberry Perl) | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | =item Borland's tools on Windows | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | =item QNX | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | =back | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | =head1 WARNINGS, BUGS and FEEDBACK | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | This is a very early release intended primarily for feedback from | 
| 557 |  |  |  |  |  |  | people who have discussed it.  The interface may change and it has | 
| 558 |  |  |  |  |  |  | not been adequately tested. | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | Feedback is most welcome, including constructive criticism. | 
| 561 |  |  |  |  |  |  | Bug reports should be made using L or by email. | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | When submitting a bug report, please include the output from running: | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  | perl -V | 
| 566 |  |  |  |  |  |  | perl -MDevel::CheckLib -e0 | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | L | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | L | 
| 573 |  |  |  |  |  |  |  | 
| 574 |  |  |  |  |  |  | =head1 AUTHORS | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  | David Cantrell Edavid@cantrell.org.ukE | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | David Golden Edagolden@cpan.orgE | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | Yasuhiro Matsumoto Emattn@cpan.orgE | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | Thanks to the cpan-testers-discuss mailing list for prompting us to write it | 
| 583 |  |  |  |  |  |  | in the first place; | 
| 584 |  |  |  |  |  |  |  | 
| 585 |  |  |  |  |  |  | to Chris Williams for help with Borland support; | 
| 586 |  |  |  |  |  |  |  | 
| 587 |  |  |  |  |  |  | to Tony Cook for help with Microsoft compiler command-line options | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  | =head1 COPYRIGHT and LICENCE | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | Copyright 2007 David Cantrell. Portions copyright 2007 David Golden. | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | This module is free-as-in-speech software, and may be used, distributed, | 
| 594 |  |  |  |  |  |  | and modified under the same conditions as perl itself. | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | =head1 CONSPIRACY | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | This module is also free-as-in-mason software. | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | =cut | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | 1; |