]>
Commit | Line | Data |
---|---|---|
47f35d89 | 1 | # $Id: CheckLib.pm,v 1.25 2008/10/27 12:16:23 drhyde Exp $ |
8b4e133f TC |
2 | # This is a modified version of Devel::CheckLib 0.93 including the patches from |
3 | # RT issues 60176 and 61645 | |
47f35d89 TC |
4 | |
5 | package # | |
6 | Devel::CheckLib; | |
7 | ||
8b4e133f | 8 | use 5.00405; #postfix foreach |
47f35d89 TC |
9 | use strict; |
10 | use vars qw($VERSION @ISA @EXPORT); | |
8b4e133f TC |
11 | $VERSION = '0.93_001'; |
12 | use Config qw(%Config); | |
13 | use Text::ParseWords 'quotewords'; | |
47f35d89 TC |
14 | |
15 | use File::Spec; | |
16 | use File::Temp; | |
17 | ||
18 | require Exporter; | |
19 | @ISA = qw(Exporter); | |
8b4e133f | 20 | @EXPORT = qw(assert_lib check_lib_or_exit check_lib); |
47f35d89 TC |
21 | |
22 | # localising prevents the warningness leaking out of this module | |
23 | local $^W = 1; # use warnings is a 5.6-ism | |
24 | ||
25 | _findcc(); # bomb out early if there's no compiler | |
26 | ||
27 | =head1 NAME | |
28 | ||
29 | Devel::CheckLib - check that a library is available | |
30 | ||
31 | =head1 DESCRIPTION | |
32 | ||
33 | Devel::CheckLib is a perl module that checks whether a particular C | |
34 | library and its headers are available. | |
35 | ||
36 | =head1 SYNOPSIS | |
37 | ||
38 | use Devel::CheckLib; | |
39 | ||
40 | check_lib_or_exit( lib => 'jpeg', header => 'jpeglib.h' ); | |
41 | check_lib_or_exit( lib => [ 'iconv', 'jpeg' ] ); | |
42 | ||
43 | # or prompt for path to library and then do this: | |
44 | check_lib_or_exit( lib => 'jpeg', libpath => $additional_path ); | |
45 | ||
46 | =head1 USING IT IN Makefile.PL or Build.PL | |
47 | ||
48 | If you want to use this from Makefile.PL or Build.PL, do | |
49 | not simply copy the module into your distribution as this may cause | |
50 | problems when PAUSE and search.cpan.org index the distro. Instead, use | |
51 | the use-devel-checklib script. | |
52 | ||
53 | =head1 HOW IT WORKS | |
54 | ||
55 | You pass named parameters to a function, describing to it how to build | |
56 | and link to the libraries. | |
57 | ||
58 | It works by trying to compile some code - which defaults to this: | |
59 | ||
60 | int main(void) { return 0; } | |
61 | ||
62 | and linking it to the specified libraries. If something pops out the end | |
63 | which looks executable, it gets executed, and if main() returns 0 we know | |
64 | that it worked. That tiny program is | |
65 | built once for each library that you specify, and (without linking) once | |
66 | for each header file. | |
67 | ||
68 | If you want to check for the presence of particular functions in a | |
69 | library, or even that those functions return particular results, then | |
70 | you can pass your own function body for main() thus: | |
71 | ||
72 | check_lib_or_exit( | |
73 | function => 'foo();if(libversion() > 5) return 0; else return 1;' | |
74 | incpath => ... | |
75 | libpath => ... | |
76 | lib => ... | |
77 | header => ... | |
78 | ); | |
79 | ||
80 | In that case, it will fail to build if either foo() or libversion() don't | |
81 | exist, and main() will return the wrong value if libversion()'s return | |
82 | value isn't what you want. | |
83 | ||
84 | =head1 FUNCTIONS | |
85 | ||
86 | All of these take the same named parameters and are exported by default. | |
87 | To avoid exporting them, C<use Devel::CheckLib ()>. | |
88 | ||
89 | =head2 assert_lib | |
90 | ||
91 | This takes several named parameters, all of which are optional, and dies | |
92 | with an error message if any of the libraries listed can | |
93 | not be found. B<Note>: dying in a Makefile.PL or Build.PL may provoke | |
94 | a 'FAIL' report from CPAN Testers' automated smoke testers. Use | |
95 | C<check_lib_or_exit> instead. | |
96 | ||
97 | The named parameters are: | |
98 | ||
99 | =over | |
100 | ||
101 | =item lib | |
102 | ||
103 | Must be either a string with the name of a single | |
104 | library or a reference to an array of strings of library names. Depending | |
105 | on the compiler found, library names will be fed to the compiler either as | |
106 | C<-l> arguments or as C<.lib> file names. (E.g. C<-ljpeg> or C<jpeg.lib>) | |
107 | ||
108 | =item libpath | |
109 | ||
110 | a string or an array of strings | |
111 | representing additional paths to search for libraries. | |
112 | ||
113 | =item LIBS | |
114 | ||
115 | a C<ExtUtils::MakeMaker>-style space-seperated list of | |
116 | libraries (each preceded by '-l') and directories (preceded by '-L'). | |
117 | ||
118 | This can also be supplied on the command-line. | |
119 | ||
120 | =back | |
121 | ||
122 | And libraries are no use without header files, so ... | |
123 | ||
124 | =over | |
125 | ||
126 | =item header | |
127 | ||
128 | Must be either a string with the name of a single | |
129 | header file or a reference to an array of strings of header file names. | |
130 | ||
131 | =item incpath | |
132 | ||
133 | a string or an array of strings | |
134 | representing additional paths to search for headers. | |
135 | ||
136 | =item INC | |
137 | ||
138 | a C<ExtUtils::MakeMaker>-style space-seperated list of | |
139 | incpaths, each preceded by '-I'. | |
140 | ||
141 | This can also be supplied on the command-line. | |
142 | ||
143 | =back | |
144 | ||
8b4e133f TC |
145 | If you need to perform know more than "does it link?" you can provide |
146 | code to be compiled and run: | |
147 | ||
148 | =over | |
149 | ||
150 | =item function | |
151 | ||
152 | the body of the <main()> function. If not provided C<return 0;> is | |
153 | used. | |
154 | ||
155 | =item prologue | |
156 | ||
157 | code to insert between the C<#include> of the headers and the | |
158 | definition of main. | |
159 | ||
160 | =back | |
161 | ||
47f35d89 TC |
162 | =head2 check_lib_or_exit |
163 | ||
164 | This behaves exactly the same as C<assert_lib()> except that instead of | |
165 | dieing, it warns (with exactly the same error message) and exits. | |
166 | This is intended for use in Makefile.PL / Build.PL | |
167 | when you might want to prompt the user for various paths and | |
168 | things before checking that what they've told you is sane. | |
169 | ||
170 | If any library or header is missing, it exits with an exit value of 0 to avoid | |
171 | causing a CPAN Testers 'FAIL' report. CPAN Testers should ignore this | |
172 | result -- which is what you want if an external library dependency is not | |
173 | available. | |
174 | ||
8b4e133f TC |
175 | =head2 check_lib |
176 | ||
177 | This behaves exactly the same as C<assert_lib()> except that it is silent, | |
178 | returning false instead of dieing, or true otherwise. | |
179 | ||
47f35d89 TC |
180 | =cut |
181 | ||
182 | sub check_lib_or_exit { | |
183 | eval 'assert_lib(@_)'; | |
184 | if($@) { | |
185 | warn $@; | |
186 | exit; | |
187 | } | |
188 | } | |
189 | ||
8b4e133f TC |
190 | sub check_lib { |
191 | eval 'assert_lib(@_)'; | |
192 | return $@ ? 0 : 1; | |
193 | } | |
194 | ||
47f35d89 TC |
195 | sub assert_lib { |
196 | my %args = @_; | |
197 | my (@libs, @libpaths, @headers, @incpaths); | |
198 | ||
199 | # FIXME: these four just SCREAM "refactor" at me | |
200 | @libs = (ref($args{lib}) ? @{$args{lib}} : $args{lib}) | |
201 | if $args{lib}; | |
202 | @libpaths = (ref($args{libpath}) ? @{$args{libpath}} : $args{libpath}) | |
203 | if $args{libpath}; | |
204 | @headers = (ref($args{header}) ? @{$args{header}} : $args{header}) | |
205 | if $args{header}; | |
206 | @incpaths = (ref($args{incpath}) ? @{$args{incpath}} : $args{incpath}) | |
207 | if $args{incpath}; | |
208 | ||
8b4e133f TC |
209 | # each entry is an arrayref containing: |
210 | # 0: arrayref of library search paths | |
211 | # 1: arrayref of library names | |
212 | my @link_cfgs = map [ \@libpaths, [ $_ ] ], @libs; | |
213 | ||
47f35d89 TC |
214 | # work-a-like for Makefile.PL's LIBS and INC arguments |
215 | # if given as command-line argument, append to %args | |
216 | for my $arg (@ARGV) { | |
8b4e133f | 217 | for my $mm_attr_key (qw(LIBS INC)) { |
47f35d89 TC |
218 | if (my ($mm_attr_value) = $arg =~ /\A $mm_attr_key = (.*)/x) { |
219 | # it is tempting to put some \s* into the expression, but the | |
220 | # MM command-line parser only accepts LIBS etc. followed by =, | |
221 | # so we should not be any more lenient with whitespace than that | |
222 | $args{$mm_attr_key} .= " $mm_attr_value"; | |
223 | } | |
224 | } | |
225 | } | |
226 | ||
227 | # using special form of split to trim whitespace | |
228 | if(defined($args{LIBS})) { | |
8b4e133f TC |
229 | if (ref $args{LIBS}) { |
230 | foreach my $arg (@{$args{LIBS}}) { | |
231 | my @sep = split ' ', $arg; | |
232 | my @libs = map { /^-l(.+)$/ ? $1 : () } @sep; | |
233 | my @paths = map { /^-L(.+)$/ ? $1 : () } @sep; | |
234 | push @link_cfgs, [ \@paths, \@libs ]; | |
235 | } | |
236 | } | |
237 | else { | |
238 | my @libs; | |
239 | my @paths; | |
240 | foreach my $arg (split(' ', $args{LIBS})) { | |
241 | die("LIBS argument badly-formed: $arg\n") unless($arg =~ /^-l/i); | |
242 | push @{$arg =~ /^-l/ ? \@libs : \@paths}, substr($arg, 2); | |
243 | } | |
244 | push @link_cfgs, map [ \@paths, [ $_ ] ], @libs; | |
245 | } | |
47f35d89 TC |
246 | } |
247 | if(defined($args{INC})) { | |
8b4e133f | 248 | foreach my $arg (_shellwords($args{INC})) { |
47f35d89 TC |
249 | die("INC argument badly-formed: $arg\n") unless($arg =~ /^-I/); |
250 | push @incpaths, substr($arg, 2); | |
251 | } | |
252 | } | |
253 | ||
8b4e133f | 254 | my ($cc, $ld) = _findcc(); |
47f35d89 TC |
255 | my @missing; |
256 | my @wrongresult; | |
8b4e133f | 257 | my @use_headers; |
47f35d89 TC |
258 | |
259 | # first figure out which headers we can't find ... | |
260 | for my $header (@headers) { | |
8b4e133f | 261 | push @use_headers, $header; |
47f35d89 TC |
262 | my($ch, $cfile) = File::Temp::tempfile( |
263 | 'assertlibXXXXXXXX', SUFFIX => '.c' | |
264 | ); | |
8b4e133f TC |
265 | print $ch qq{#include <$_>\n} for @use_headers; |
266 | print $ch qq{int main(void) { return 0; }\n}; | |
47f35d89 TC |
267 | close($ch); |
268 | my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; | |
269 | my @sys_cmd; | |
270 | # FIXME: re-factor - almost identical code later when linking | |
271 | if ( $Config{cc} eq 'cl' ) { # Microsoft compiler | |
272 | require Win32; | |
273 | @sys_cmd = ( | |
8b4e133f | 274 | @$cc, |
47f35d89 TC |
275 | $cfile, |
276 | "/Fe$exefile", | |
8b4e133f TC |
277 | (map { '/I'.Win32::GetShortPathName($_) } @incpaths), |
278 | "/link", | |
279 | @$ld | |
47f35d89 TC |
280 | ); |
281 | } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland | |
282 | @sys_cmd = ( | |
8b4e133f | 283 | @$cc, |
47f35d89 TC |
284 | (map { "-I$_" } @incpaths), |
285 | "-o$exefile", | |
8b4e133f TC |
286 | $cfile, |
287 | @$ld | |
47f35d89 TC |
288 | ); |
289 | } else { # Unix-ish: gcc, Sun, AIX (gcc, cc), ... | |
290 | @sys_cmd = ( | |
8b4e133f TC |
291 | @$cc, |
292 | @$ld, | |
47f35d89 TC |
293 | $cfile, |
294 | (map { "-I$_" } @incpaths), | |
295 | "-o", "$exefile" | |
296 | ); | |
297 | } | |
298 | warn "# @sys_cmd\n" if $args{debug}; | |
299 | my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); | |
300 | push @missing, $header if $rv != 0 || ! -x $exefile; | |
301 | _cleanup_exe($exefile); | |
302 | unlink $cfile; | |
303 | } | |
304 | ||
305 | # now do each library in turn with headers | |
306 | my($ch, $cfile) = File::Temp::tempfile( | |
307 | 'assertlibXXXXXXXX', SUFFIX => '.c' | |
308 | ); | |
309 | print $ch qq{#include <$_>\n} foreach (@headers); | |
8b4e133f | 310 | print $ch "\n$args{prologue}\n" if $args{prologue}; |
47f35d89 TC |
311 | print $ch "int main(void) { ".($args{function} || 'return 0;')." }\n"; |
312 | close($ch); | |
8b4e133f TC |
313 | for my $link_cfg ( @link_cfgs ) { |
314 | my ($paths, $libs) = @$link_cfg; | |
47f35d89 TC |
315 | my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; |
316 | my @sys_cmd; | |
317 | if ( $Config{cc} eq 'cl' ) { # Microsoft compiler | |
318 | require Win32; | |
319 | my @libpath = map { | |
320 | q{/libpath:} . Win32::GetShortPathName($_) | |
321 | } @libpaths; | |
8b4e133f | 322 | # this is horribly sensitive to the order of arguments |
47f35d89 | 323 | @sys_cmd = ( |
8b4e133f | 324 | @$cc, |
47f35d89 | 325 | $cfile, |
8b4e133f | 326 | ( map { "$_.lib" } @$libs ), |
47f35d89 TC |
327 | "/Fe$exefile", |
328 | (map { '/I'.Win32::GetShortPathName($_) } @incpaths), | |
329 | "/link", | |
8b4e133f TC |
330 | @$ld, |
331 | (map {'/libpath:'.Win32::GetShortPathName($_)} @$paths), | |
47f35d89 TC |
332 | ); |
333 | } elsif($Config{cc} eq 'CC/DECC') { # VMS | |
334 | } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland | |
335 | @sys_cmd = ( | |
8b4e133f TC |
336 | @$cc, |
337 | @$ld, | |
47f35d89 | 338 | "-o$exefile", |
8b4e133f | 339 | (map { "-l$_" } @$libs ), |
47f35d89 | 340 | (map { "-I$_" } @incpaths), |
8b4e133f | 341 | (map { "-L$_" } @$paths), |
47f35d89 TC |
342 | $cfile); |
343 | } else { # Unix-ish | |
344 | # gcc, Sun, AIX (gcc, cc) | |
345 | @sys_cmd = ( | |
8b4e133f TC |
346 | @$cc, |
347 | @$ld, | |
47f35d89 TC |
348 | $cfile, |
349 | "-o", "$exefile", | |
8b4e133f | 350 | (map { "-l$_" } @$libs ), |
47f35d89 | 351 | (map { "-I$_" } @incpaths), |
8b4e133f | 352 | (map { "-L$_" } @$paths) |
47f35d89 TC |
353 | ); |
354 | } | |
355 | warn "# @sys_cmd\n" if $args{debug}; | |
356 | my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); | |
8b4e133f TC |
357 | push @missing, @$libs if $rv != 0 || ! -x $exefile; |
358 | my $absexefile = File::Spec->rel2abs($exefile); | |
359 | $absexefile = '"'.$absexefile.'"' if $absexefile =~ m/\s/; | |
360 | push @wrongresult, @$libs if $rv == 0 && -x $exefile && system($absexefile) != 0; | |
47f35d89 TC |
361 | _cleanup_exe($exefile); |
362 | } | |
363 | unlink $cfile; | |
364 | ||
365 | my $miss_string = join( q{, }, map { qq{'$_'} } @missing ); | |
366 | die("Can't link/include $miss_string\n") if @missing; | |
367 | my $wrong_string = join( q{, }, map { qq{'$_'} } @wrongresult); | |
368 | die("wrong result: $wrong_string\n") if @wrongresult; | |
369 | } | |
370 | ||
371 | sub _cleanup_exe { | |
372 | my ($exefile) = @_; | |
373 | my $ofile = $exefile; | |
374 | $ofile =~ s/$Config{_exe}$/$Config{_o}/; | |
375 | unlink $exefile if -f $exefile; | |
376 | unlink $ofile if -f $ofile; | |
377 | unlink "$exefile\.manifest" if -f "$exefile\.manifest"; | |
8b4e133f TC |
378 | if ( $Config{cc} eq 'cl' ) { |
379 | # MSVC also creates foo.ilk and foo.pdb | |
380 | my $ilkfile = $exefile; | |
381 | $ilkfile =~ s/$Config{_exe}$/.ilk/; | |
382 | my $pdbfile = $exefile; | |
383 | $pdbfile =~ s/$Config{_exe}$/.pdb/; | |
384 | unlink $ilkfile if -f $ilkfile; | |
385 | unlink $pdbfile if -f $pdbfile; | |
386 | } | |
47f35d89 TC |
387 | return |
388 | } | |
8b4e133f TC |
389 | |
390 | # return ($cc, $ld) | |
391 | # where $cc is an array ref of compiler name, compiler flags | |
392 | # where $ld is an array ref of linker flags | |
47f35d89 | 393 | sub _findcc { |
8b4e133f TC |
394 | # Need to use $keep=1 to work with MSWin32 backslashes and quotes |
395 | my $Config_ccflags = $Config{ccflags}; # use copy so ASPerl will compile | |
396 | my @Config_ldflags = @Config{qw(ldflags perllibs)}; | |
397 | my @ccflags = grep { length } quotewords('\s+', 1, $Config_ccflags); | |
398 | my @ldflags = grep { length } quotewords('\s+', 1, @Config_ldflags); | |
47f35d89 TC |
399 | my @paths = split(/$Config{path_sep}/, $ENV{PATH}); |
400 | my @cc = split(/\s+/, $Config{cc}); | |
8b4e133f | 401 | return ( [ @cc, @ccflags ], \@ldflags ) if -x $cc[0]; |
47f35d89 TC |
402 | foreach my $path (@paths) { |
403 | my $compiler = File::Spec->catfile($path, $cc[0]) . $Config{_exe}; | |
8b4e133f TC |
404 | return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags) |
405 | if -x $compiler; | |
47f35d89 TC |
406 | } |
407 | die("Couldn't find your C compiler\n"); | |
408 | } | |
409 | ||
8b4e133f TC |
410 | sub _shellwords { |
411 | my $line = shift; | |
412 | ||
413 | if ($^O eq "MSWin32") { | |
414 | my @elements; | |
415 | $line =~ s/^\s+//; | |
416 | while ($line =~ s/^"([^"]*)"// || $line =~ s/^(\S+)//) { | |
417 | push @elements, $1; | |
418 | $line =~ s/^\s+//; | |
419 | } | |
420 | return @elements; | |
421 | } | |
422 | else { | |
423 | return grep defined && /\S/, quotewords('\s+', 0, $line); | |
424 | } | |
425 | } | |
426 | ||
47f35d89 TC |
427 | # code substantially borrowed from IPC::Run3 |
428 | sub _quiet_system { | |
429 | my (@cmd) = @_; | |
430 | ||
431 | # save handles | |
432 | local *STDOUT_SAVE; | |
433 | local *STDERR_SAVE; | |
434 | open STDOUT_SAVE, ">&STDOUT" or die "CheckLib: $! saving STDOUT"; | |
435 | open STDERR_SAVE, ">&STDERR" or die "CheckLib: $! saving STDERR"; | |
436 | ||
437 | # redirect to nowhere | |
438 | local *DEV_NULL; | |
439 | open DEV_NULL, ">" . File::Spec->devnull | |
440 | or die "CheckLib: $! opening handle to null device"; | |
441 | open STDOUT, ">&" . fileno DEV_NULL | |
442 | or die "CheckLib: $! redirecting STDOUT to null handle"; | |
443 | open STDERR, ">&" . fileno DEV_NULL | |
444 | or die "CheckLib: $! redirecting STDERR to null handle"; | |
445 | ||
446 | # run system command | |
447 | my $rv = system(@cmd); | |
448 | ||
449 | # restore handles | |
450 | open STDOUT, ">&" . fileno STDOUT_SAVE | |
451 | or die "CheckLib: $! restoring STDOUT handle"; | |
452 | open STDERR, ">&" . fileno STDERR_SAVE | |
453 | or die "CheckLib: $! restoring STDERR handle"; | |
454 | ||
455 | return $rv; | |
456 | } | |
457 | ||
458 | =head1 PLATFORMS SUPPORTED | |
459 | ||
460 | You must have a C compiler installed. We check for C<$Config{cc}>, | |
461 | both literally as it is in Config.pm and also in the $PATH. | |
462 | ||
463 | It has been tested with varying degrees on rigourousness on: | |
464 | ||
465 | =over | |
466 | ||
467 | =item gcc (on Linux, *BSD, Mac OS X, Solaris, Cygwin) | |
468 | ||
469 | =item Sun's compiler tools on Solaris | |
470 | ||
471 | =item IBM's tools on AIX | |
472 | ||
473 | =item SGI's tools on Irix 6.5 | |
474 | ||
475 | =item Microsoft's tools on Windows | |
476 | ||
477 | =item MinGW on Windows (with Strawberry Perl) | |
478 | ||
479 | =item Borland's tools on Windows | |
480 | ||
481 | =item QNX | |
482 | ||
483 | =back | |
484 | ||
485 | =head1 WARNINGS, BUGS and FEEDBACK | |
486 | ||
487 | This is a very early release intended primarily for feedback from | |
488 | people who have discussed it. The interface may change and it has | |
489 | not been adequately tested. | |
490 | ||
491 | Feedback is most welcome, including constructive criticism. | |
492 | Bug reports should be made using L<http://rt.cpan.org/> or by email. | |
493 | ||
494 | When submitting a bug report, please include the output from running: | |
495 | ||
496 | perl -V | |
497 | perl -MDevel::CheckLib -e0 | |
498 | ||
499 | =head1 SEE ALSO | |
500 | ||
501 | L<Devel::CheckOS> | |
502 | ||
503 | L<Probe::Perl> | |
504 | ||
505 | =head1 AUTHORS | |
506 | ||
507 | David Cantrell E<lt>david@cantrell.org.ukE<gt> | |
508 | ||
509 | David Golden E<lt>dagolden@cpan.orgE<gt> | |
510 | ||
8b4e133f TC |
511 | Yasuhiro Matsumoto E<lt>mattn@cpan.orgE<gt> |
512 | ||
47f35d89 TC |
513 | Thanks to the cpan-testers-discuss mailing list for prompting us to write it |
514 | in the first place; | |
515 | ||
8b4e133f TC |
516 | to Chris Williams for help with Borland support; |
517 | ||
518 | to Tony Cook for help with Microsoft compiler command-line options | |
47f35d89 TC |
519 | |
520 | =head1 COPYRIGHT and LICENCE | |
521 | ||
522 | Copyright 2007 David Cantrell. Portions copyright 2007 David Golden. | |
523 | ||
524 | This module is free-as-in-speech software, and may be used, distributed, | |
525 | and modified under the same conditions as perl itself. | |
526 | ||
527 | =head1 CONSPIRACY | |
528 | ||
529 | This module is also free-as-in-mason software. | |
530 | ||
531 | =cut | |
532 | ||
533 | 1; |