1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
|
#!/usr/bin/perl
# Configure.pm. Version 1.00 Copyright (C) 1995, Kenneth Albanowski
#
# You are welcome to use this code in your own perl modules, I just
# request that you don't distribute modified copies without making it clear
# that you have changed something. If you have a change you think is worth
# merging into the original, please contact me at kjahds@kjahds.com or
# CIS:70705,126
#
# $Id: Configure.pm,v 1.2 2002/01/28 18:40:18 gellyfish Exp $
#
# Todo: clean up redudant code in CPP, Compile, Link, and Execute
#
package Configure;
use strict;
use vars qw(@EXPORT @ISA);
use Carp;
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw( CPP
Compile
Link
Execute
FindHeader
FindLib
Apply
ApplyHeaders
ApplyLibs
ApplyHeadersAndLibs
ApplyHeadersAndLibsAndExecute
CheckHeader
CheckStructure
CheckField
CheckHSymbol
CheckSymbol
CheckLSymbol
GetSymbol
GetTextSymbol
GetNumericSymbol
GetConstants);
use Cwd;
use Config;
my ($C_usrinc, $C_libpth, $C_cppstdin, $C_cppflags, $C_cppminus,
$C_ccflags,$C_ldflags,$C_cc,$C_libs) =
@Config{qw( usrinc libpth cppstdin cppflags cppminus
ccflags ldflags cc libs)};
my $Verbose = 0;
=head1 NAME
Configure.pm - provide auto-configuration utilities
=head1 SUMMARY
This perl module provides tools to figure out what is present in the C
compilation environment. This is intended mostly for perl extensions to use
to configure themselves. There are a number of functions, with widely varying
levels of specificity, so here is a summary of what the functions can do:
CheckHeader: Look for headers.
CheckStructure: Look for a structure.
CheckField: Look for a field in a structure.
CheckHSymbol: Look for a symbol in a header.
CheckLSymbol: Look for a symbol in a library.
CheckSymbol: Look for a symbol in a header and library.
GetTextSymbol: Get the contents of a symbol as text.
GetNumericSymbol: Get the contents of a symbol as a number.
Apply: Try compiling code with a set of headers and libs.
ApplyHeaders: Try compiling code with a set of headers.
ApplyLibraries: Try linking code with a set of libraries.
ApplyHeadersAndLibaries: You get the idea.
ApplyHeadersAndLibariesAnExecute: You get the idea.
CPP: Feed some code through the C preproccessor.
Compile: Try to compile some C code.
Link: Try to compile & link some C code.
Execute: Try to compile, link, & execute some C code.
=head1 FUNCTIONS
=cut
# Here we go into the actual functions
=head2 CPP
Takes one or more arguments. The first is a string containing a C program.
Embedded newlines are legal, the text simply being stuffed into a temporary
file. The result is then fed to the C preproccessor (that preproccessor being
previously determined by perl's Configure script.) Any additional arguments
provided are passed to the preprocessing command.
In a scalar context, the return value is either undef, if something went wrong,
or the text returned by the preprocessor. In an array context, two values are
returned: the numeric exit status and the output of the preproccessor.
=cut
sub CPP { # Feed code to preproccessor, returning error value and output
my($code,@options) = @_;
my($options) = join(" ",@options);
my($file) = "tmp$$";
my($in,$out) = ($file.".c",$file.".o");
open(F,">$in");
print F $code;
close(F);
print "Preprocessing |$code|\n" if $Verbose;
my($result) = scalar(`$C_cppstdin $C_cppflags $C_cppminus $options < $in 2>/dev/null`);
print "Executing '$C_cppstdin $C_cppflags $C_cppminus $options < $in 2>/dev/null'\n" if $Verbose;
my($error) = $?;
print "Returned |$result|\n" if $Verbose;
unlink($in,$out);
return ($error ? undef : $result) unless wantarray;
($error,$result);
}
=head2 Compile
Takes one or more arguments. The first is a string containing a C program.
Embedded newlines are legal, the text simply being stuffed into a temporary
file. The result is then fed to the C compiler (that compiler being
previously determined by perl's Configure script.) Any additional arguments
provided are passed to the compiler command.
In a scalar context, either 0 or 1 will be returned, with 1 indicating a
successful compilation. In an array context, three values are returned: the
numeric exit status of the compiler, a string consisting of the output
generated by the compiler, and a numeric value that is false if a ".o" file
wasn't produced by the compiler, error status or no.
=cut
sub Compile { # Feed code to compiler. On error, return status and text
my($code,@options) = @_;
my($options)=join(" ",@options);
my($file) = "tmp$$";
my($in,$out) = ($file.".c",$file.".o");
open(F,">$in");
print F $code;
close(F);
print "Compiling |$code|\n" if $Verbose;
my($result) = scalar(`$C_cc $C_ccflags -c $in $C_ldflags $C_libs $options 2>&1`);
print "Executing '$C_cc $C_ccflags -c $in $C_ldflags $C_libs $options 2>&1'\n" if $Verbose;
my($error) = $?;
my($error2) = ! -e $out;
unlink($in,$out);
return (($error || $error2) ? 0 : 1) unless wantarray;
($error,$result,$error2);
}
=head2 Link
Takes one or more arguments. The first is a string containing a C program.
Embedded newlines are legal, the text simply being stuffed into a temporary
file. The result is then fed to the C compiler and linker (that compiler and
linker being previously determined by perl's Configure script.) Any
additional arguments provided are passed to the compilation/link command.
In a scalar context, either 0 or 1 is returned, with 1 indicating a
successful compilation. In an array context, two values are returned: the
numeric exit status of the compiler/linker, and a string consisting of the
output generated by the compiler/linker.
Note that this command I<only> compiles and links the C code. It does not
attempt to execute it.
=cut
sub Link { # Feed code to compiler and linker. On error, return status and text
my($code,@options) = @_;
my($options) = join(" ",@options);
my($file) = "tmp$$";
my($in,$out) = $file.".c",$file.".o";
open(F,">$in");
print F $code;
close(F);
print "Linking |$code|\n" if $Verbose;
my($result) = scalar(`$C_cc $C_ccflags -o $file $in $C_ldflags $C_libs $options 2>&1`);
print "Executing '$C_cc $C_ccflags -o $file $in $C_ldflags $C_libs $options 2>&1'\n" if $Verbose;
my($error)=$?;
print "Error linking: $error, |$result|\n" if $Verbose;
unlink($in,$out,$file);
return (($error || $result ne "")?0:1) unless wantarray;
($error,$result);
}
=head2 Execute
Takes one or more arguments. The first is a string containing a C program.
Embedded newlines are legal, the text simply being stuffed into a temporary
file. The result is then fed to the C compiler and linker (that compiler and
linker being previously determined by perl's metaconfig script.) and then
executed. Any additional arguments provided are passed to the
compilation/link command. (There is no way to feed arguments to the program
being executed.)
In a scalar context, the return value is either undef, indicating the
compilation or link failed, or that the executed program returned a nonzero
status. Otherwise, the return value is the text output by the program.
In an array context, an array consisting of three values is returned: the
first value is 0 or 1, 1 if the compile/link succeeded. The second value either
the exist status of the compiler or program, and the third is the output text.
=cut
sub Execute { #Compile, link, and execute.
my($code,@options) = @_;
my($options)=join(" ",@options);
my($file) = "tmp$$";
my($in,$out) = $file.".c",$file.".o";
open(F,">$in");
print F $code;
close(F);
print "Executing |$code|\n" if $Verbose;
my($result) = scalar(`$C_cc $C_ccflags -o $file $in $C_ldflags $C_libs $options 2>&1`);
print "Executing '$C_cc $C_ccflags -o $file $in $C_ldflags $C_libs $options 2>&1'\n" if $Verbose;
my($error) = $?;
unlink($in,$out);
if(!$error) {
my($result2) = scalar(`./$file`);
$error = $?;
unlink($file);
return ($error?undef:$result2) unless wantarray;
print "Executed successfully, status $error, link $result, exec |$result2|\n" if $Verbose;
(1,$error,$result2);
} else {
print "Link failed, status $error, message |$result|\n" if $Verbose;
return undef unless wantarray;
(0,$error,$result);
}
}
=head2 FindHeader
Takes an unlimited number of arguments, consisting of both header names in
the form "header.h", or directory specifications such as "-I/usr/include/bsd".
For each supplied header, FindHeader will attempt to find the complete path.
The return value is an array consisting of all the headers that were located.
=cut
sub FindHeader { #For each supplied header name, find full path
my(@headers) = grep(!/^-I/,@_);
my(@I) = grep(/^-I/,@_);
my($h);
for $h (@headers) {
print "Searching for $h... " if $Verbose;
if($h eq "") {$h=undef; next}
if( -f $h) {next}
if( -f $Config{"usrinc"}."/".$h) {
$h = $Config{"usrinc"}."/".$h;
print "Found as $h.\n" if $Verbose;
} else {
my $text;
if($text = CPP("#include <$h>",join(" ",@I))) {
grepcpp:
for (split(/\s+/,(grep(/^\s*#.*$h/,split(/\n/,$text)))[0])) {
if(/$h/) {
s/^\"(.*)\"$/$1/;
s/^\'(.*)\'$/$1/;
$h = $_;
print "Found as $h.\n" if $Verbose;
last grepcpp;
}
}
} else {
$h = undef; # remove header from resulting list
print "Not found.\n" if $Verbose;
}
}
}
grep($_,@headers);
}
=head2 FindLib
Takes an unlimited number of arguments, consisting of both library names in
the form "-llibname", "/usr/lib/libxyz.a" or "dld", or directory
specifications such as "-L/usr/lib/foo". For each supplied library, FindLib
will attempt to find the complete path. The return value is an array
consisting of the full paths to all of the libraries that were located.
=cut
sub FindLib { #For each supplied library name, find full path
my(@libs) = grep(!/^-L/,@_);
my(@L) = (grep(/^-L/,@_),split(" ",$Config{"libpth"}));
grep(s/^-L//,@L);
my($l);
my($so) = $Config{"so"};
my($found);
#print "Libaries I am searching for: ",join(",",@libs),"\n";
#print "Directories: ",join(",",@L),"\n";
my $lib;
for $lib (@libs) {
print "Searching for $lib... " if $Verbose;
$found=0;
$lib =~ s/^-l//;
if($lib eq "") {$lib=undef; next}
next if -f $lib;
my $path;
for $path (@L) {
my ( $fullname, @fullname );
print "Searching $path for $lib...\n" if $Verbose;
if (@fullname=<${path}/lib${lib}.${so}.[0-9]*>){
$fullname=$fullname[-1]; #ATTN: 10 looses against 9!
} elsif (-f ($fullname="$path/lib$lib.$so")){
} elsif (-f ($fullname="$path/lib${lib}_s.a")
&& ($lib .= "_s") ){ # we must explicitly ask for _s version
} elsif (-f ($fullname="$path/lib$lib.a")){
} elsif (-f ($fullname="$path/Slib$lib.a")){
} else {
warn "$lib not found in $path\n" if $Verbose;
next;
}
warn "'-l$lib' found at $fullname\n" if $Verbose;
$lib = $fullname;
$found=1;
}
if(!$found) {
$lib = undef; # Remove lib if not found
print "Not found.\n" if $Verbose;
}
}
grep($_,@libs);
}
=head2
Apply takes a chunk of code, a series of libraries and headers, and attempts
to apply them, in series, to a given perl command. In a scalar context, the
return value of the first set of headers and libraries that produces a
non-zero return value from the command is returned. In an array context, the
header and library set it returned.
This is best explained by some examples:
Apply(\&Compile,"main(){}","sgtty.h","");
In a scalar context either C<undef> or C<1>. In an array context,
this returns C<()> or C<("sgtty.h","")>.
Apply(\&Link,"main(){int i=COLOR_PAIRS;}","curses.h","-lcurses",
"ncurses.h","-lncurses","ncurses/ncurses.h","-lncurses");
In a scalar context, this returns either C<undef>, C<1>. In an array context,
this returns C<("curses.h","-lcurses")>, C<("ncurses.h","-lncurses")>,
C<("ncurses/ncurses.h","-lncurses")>, or C<()>.
If we had instead said
C<Apply(\&Execute,'main(){printf("%d",(int)COLOR_PAIRS)',...)> then in a scalar
context either C<undef> or the value of COLOR_PAIRS would be returned.
Note that you can also supply multiple headers and/or libraries at one time,
like this:
Apply(\&Compile,"main(){fcntl(0,F_GETFD);}","fcntl.h","",
"ioctl.h fcntl.h","","sys/ioctl.h fcntl.h"","");
So if fcntl needs ioctl or sys/ioctl loaded first, this will catch it. In an
array context, C<()>, C<("fcntl.h","")>, C<("ioctl.h fcntl.h","")>, or
C<("sys/ioctl.h fcntl.h","")> could be returned.
You can also use nested arrays to get exactly the same effect. The returned
array will always consist of a string, though, with elements separated by
spaces.
Apply(\&Compile,"main(){fcntl(0,F_GETFD);}",["fcntl.h"],"",
["ioctl.h","fcntl.h"],"",["sys/ioctl.h","fcntl.h"],"");
Note that there are many functions that provide simpler ways of doing these
things, from GetNumericSymbol to get the value of a symbol, to ApplyHeaders
which doesn't ask for libraries.
=cut
sub Apply { #
my($cmd,$code,@lookup) = @_;
my(@l,@h,$i,$ret);
for ($i=0;$i<@lookup;$i+=2) {
if( ref($lookup[$i]) eq "ARRAY" ) {
@h = @{$lookup[$i]};
} else {
@h = split(/\s+/,$lookup[$i]);
}
if( ref($lookup[$i+1]) eq "ARRAY" ) {
@l = @{$lookup[$i+1]};
} else {
@l = split(/\s+/,$lookup[$i+1]);
}
if($ret=&{$cmd == \&Link && !@l?\&Compile:$cmd}(join("",map($_?"#include <$_>\n":"",grep(!/^-I/,@h))).
$code,grep(/^-I/,@h),@l)) {
print "Ret=|$ret|\n" if $Verbose;
return $ret unless wantarray;
return (join(" ",@h),join(" ",@l));
}
}
return 0 unless wantarray;
();
}
=head2 ApplyHeadersAndLibs
This function takes the same sort of arguments as Apply, it just sends them
directly to Link.
=cut
sub ApplyHeadersAndLibs { #
my($code,@lookup) = @_;
Apply \&Link,$code,@lookup;
}
=head2 ApplyHeadersAndLibsAndExecute
This function is similar to Apply and ApplyHeadersAndLibs, but it always
uses Execute.
=cut
sub ApplyHeadersAndLibsAndExecute { #
my($code,@lookup) = @_;
Apply \&Execute,$code,@lookup;
}
=head2 ApplyHeaders
If you are only checking headers, and don't need to look at libs, then
you will probably want to use ApplyHeaders. The return value is the same
in a scalar context, but in an array context the returned array will only
consists of the headers, spread out.
=cut
sub ApplyHeaders {
my($code,@headers) = @_;
return scalar(ApplyHeadersAndLibs $code, map(($_,""),@headers))
unless wantarray;
split(/\s+/,(ApplyHeadersAndLibs $code, map(($_,""),@headers))[0]);
}
=head2 ApplyLibs
If you are only checking libraries, and don't need to look at headers, then
you will probably want to use ApplyLibs. The return value is the same
in a scalar context, but in an array context the returned array will only
consists of the libraries, spread out.
=cut
sub ApplyLibs {
my($code,@libs) = @_;
return scalar(ApplyHeadersAndLibs $code, map(("",$_),@libs))
unless wantarray;
split(/\s+/,(ApplyHeadersAndLibs $code, map(("",$_),@libs))[0]);
}
=head2 CheckHeader
Takes an unlimited number of arguments, consiting of headers in the
Apply style. The first set that is fully accepted
by the compiler is returned.
=cut
sub CheckHeader { #Find a header (or set of headers) that exists
ApplyHeaders("main(){}",@_);
}
=head2 CheckStructure
Takes the name of a structure, and an unlimited number of further arguments
consisting of header groups. The first group that defines that structure
properly will be returned. B<undef> will be returned if nothing succeeds.
=cut
sub CheckStructure { # Check existance of a structure.
my($structname,@headers) = @_;
ApplyHeaders("main(){ struct $structname s;}",@headers);
}
=head2 CheckField
Takes the name of a structure, the name of a field, and an unlimited number
of further arguments consisting of header groups. The first group that
defines a structure that contains the field will be returned. B<undef> will
be returned if nothing succeeds.
=cut
sub CheckField { # Check for the existance of specified field in structure
my($structname,$fieldname,@headers) = @_;
ApplyHeaders("main(){ struct $structname s1; struct $structname s2;
s1.$fieldname = s2.$fieldname; }",@headers);
}
=head2 CheckLSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of library groups. The first group of libraries that defines
that symbol will be returned. B<undef> will be returned if nothing succeeds.
=cut
sub CheckLSymbol { # Check for linkable symbol
my($symbol,@libs) = @_;
ApplyLibs("main() { void * f = (void *)($symbol); }",@libs);
}
=head2 CheckSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of header and library groups, in the Apply format. The first
group of headers and libraries that defines that symbol will be returned.
B<undef> will be returned if nothing succeeds.
=cut
sub CheckSymbol { # Check for linkable/header symbol
my($symbol,@lookup) = @_;
ApplyHeadersAndLibs("main() { void * f = (void *)($symbol); }",@lookup);
}
=head2 CheckHSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of header groups. The first group of headers that defines
that symbol will be returned. B<undef> will be returned if nothing succeeds.
=cut
sub CheckHSymbol { # Check for header symbol
my($symbol,@headers) = @_;
ApplyHeaders("main() { void * f = (void *)($symbol); }",@headers);
}
=head2 CheckHPrototype (unexported)
An experimental routine that takes a name of a function, a nested array
consisting of the prototype, and then the normal header groups. It attempts
to deduce whether the given prototype matches what the header supplies.
Basically, it doesn't work. Or maybe it does. I wouldn't reccomend it,
though.
=cut
sub CheckHPrototype { # Check for header prototype.
# Note: This function is extremely picky about "const int" versus "int",
# and depends on having an extremely snotty compiler. Anything but GCC
# may fail, and even GCC may not work properly. In any case, if the
# names function doesn't exist, this call will _succeed_. Caveat Utilitor.
my($function,$proto,@headers) = @_;
my(@proto) = @{$proto};
ApplyHeaders("main() { extern ".$proto[0]." $function(".
join(",",@proto[1..$#proto])."); }",@headers);
}
=head2 GetSymbol
Takes the name of a symbol, a printf command, a cast, and an unlimited
number of further arguments consisting of header and library groups, in the
Apply. The first group of headers and libraries that defines that symbol
will be used to get the contents of the symbol in the format, and return it.
B<undef> will be returned if nothing defines that symbol.
Example:
GetSymbol("__LINE__","ld","long","","");
=cut
sub GetSymbol { # Check for linkable/header symbol
my($symbol,$printf,$cast,@lookup) = @_,"","";
scalar(ApplyHeadersAndLibsAndExecute(
"main(){ printf(\"\%$printf\",($cast)($symbol));exit(0);}",@lookup));
}
=head2 GetTextSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of header and library groups, in the ApplyHeadersAndLibs format.
The first group of headers and libraries that defines that symbol will be
used to get the contents of the symbol in text format, and return it.
B<undef> will be returned if nothing defines that symbol.
Note that the symbol I<must> actually be text, either a char* or a constant
string. Otherwise, the results are undefined.
=cut
sub GetTextSymbol { # Check for linkable/header symbol
my($symbol,@lookup) = @_,"","";
my($result) = GetSymbol($symbol,"s","char*",@lookup);
$result .= "" if defined($result);
$result;
}
=head2 GetNumericSymbol
Takes the name of a symbol, and an unlimited number of further arguments
consisting of header and library groups, in the ApplyHeadersAndLibs format.
The first group of headers and libraries that defines that symbol will be
used to get the contents of the symbol in numeric format, and return it.
B<undef> will be returned if nothing defines that symbol.
Note that the symbol I<must> actually be numeric, in a format compatible
with a float. Otherwise, the results are undefined.
=cut
sub GetNumericSymbol { # Check for linkable/header symbol
my($symbol,@lookup) = @_,"","";
my($result) = GetSymbol($symbol,"f","float",@lookup);
$result += 0 if defined($result);
$result;
}
=head2 GetConstants
Takes a list of header names (possibly including -I directives) and attempts
to grep the specified files for constants, a constant being something #defined
with a name that matches /[A-Z0-9_]+/. Returns the list of names.
=cut
sub GetConstants { # Try to grep constants out of a header
my(@headers) = @_;
@headers = FindHeader(@headers);
my %seen;
my(%results);
map($seen{$_}=1,@headers);
while(@headers) {
$_=shift(@headers);
next if !defined($_);
open(SEARCHHEADER,"<$_");
while(<SEARCHHEADER>) {
if(/^\s*#\s*define\s+([A-Z_][A-Za-z0-9_]+)\s+/) {
$results{$1} = 1;
} elsif(/^\s*#\s*include\s+[<"]?([^">]+)[>"]?/) {
my(@include) = FindHeader($1);
@include = grep(!$seen{$_},map(defined($_)?$_:(),@include));
push(@headers,@include);
map($seen{$_}=1,@include);
}
}
close(SEARCHHEADER);
}
keys %results;
}
=head2 DeducePrototype (unexported)
This one is B<really> experimental. The idea is to figure out some basic
characteristics of the compiler, and then attempt to "feel out" the prototype
of a function. Eventually, it may work. It is guaranteed to be very slow,
and it may simply not be capable of working on some systems.
=cut
my $firstdeduce = 1;
sub DeducePrototype {
my (@types, $checkreturn, $checknilargs, $checkniletcargs, $checkreturnnil);
if($firstdeduce) {
$firstdeduce=0;
my $checknumber=!Compile("extern int func(int a,int b);
extern int func(int a,int b,int c);
main(){}");
$checkreturn=!Compile("extern int func(int a,int b);
extern long func(int a,int b);
main(){}");
my $checketc= !Compile("extern int func(int a,int b);
extern long func(int a,...);
main(){}");
my $checknumberetc=!Compile("extern int func(int a,int b);
extern int func(int a,int b,...);
main(){}");
my $checketcnumber=!Compile("extern int func(int a,int b,int c,...);
extern int func(int a,int b,...);
main(){}");
my $checkargtypes=!Compile("extern int func(int a);
extern int func(long a);
main(){}");
my $checkargsnil=!Compile("extern int func();
extern int func(int a,int b,int c);
main(){}");
$checknilargs=!Compile("extern int func(int a,int b,int c);
extern int func();
main(){}");
my $checkargsniletc=!Compile("extern int func(...);
extern int func(int a,int b,int c);
main(){}");
$checkniletcargs=!Compile("extern int func(int a,int b,int c);
extern int func(...);
main(){}");
my $checkconst=!Compile("extern int func(const int * a);
extern int func(int * a);
main(){ }");
my $checksign=!Compile("extern int func(int a);
extern int func(unsigned int a);
main(){ }");
$checkreturnnil=!Compile("extern func(int a);
extern void func(int a);
main(){ }");
@types = sort grep(Compile("main(){$_ a;}"),
"void","int","long int","unsigned int","unsigned long int","long long int",
"long long","unsigned long long",
"unsigned long long int","float","long float",
"double","long double",
"char","unsigned char","short int","unsigned short int");
if(Compile("main(){flurfie a;}")) { @types = (); }
$Verbose=0;
# Attempt to remove duplicate types (if any) from type list
my ( $i, $j );
if($checkargtypes) {
for ($i=0;$i<=$#types;$i++) {
for ($j=$i+1;$j<=$#types;$j++) {
next if $j==$i;
if(Compile("extern void func($types[$i]);
extern void func($types[$j]); main(){}")) {
print "Removing type $types[$j] because it equals $types[$i]\n";
splice(@types,$j,1);
$j--;
}
}
}
} elsif($checkreturn) {
for ($i=0;$i<=$#types;$i++) {
for ($j=$i+1;$j<=$#types;$j++) {
next if $j==$i;
if(Compile("$types[$i] func(void);
extern $types[$j] func(void); main(){}")) {
print "Removing type $types[$j] because it equals $types[$i]\n";
splice(@types,$j,1);
$j--;
}
}
}
}
$Verbose=1;
print "Detect differing numbers of arguments: $checknumber\n";
print "Detect differing return types: $checkreturn\n";
print "Detect differing argument types if one is ...: $checketc\n";
print "Detect differing numbers of arguments if ... is involved: $checknumberetc\n";
print "Detect differing numbers of arguments if ... is involved #2: $checketcnumber\n";
print "Detect differing argument types: $checkargtypes\n";
print "Detect differing argument types if first has no defined args: $checkargsnil\n";
print "Detect differing argument types if second has no defined args: $checknilargs\n";
print "Detect differing argument types if first has only ...: $checkargsniletc\n";
print "Detect differing argument types if second has only ...: $checkniletcargs\n";
print "Detect differing argument types by constness: $checkconst\n";
print "Detect differing argument types by signedness: $checksign\n";
print "Detect differing return types if one is not defined: $checkreturnnil\n";
print "Types known: ",join(",",@types),"\n";
}
my($function,@headers) = @_;
@headers = CheckHSymbol($function,@headers);
return undef if !@headers;
my $rettype = undef;
my @args = ();
my @validcount = ();
# Can we check the return type without worry about arguements?
if($checkreturn and (!$checknilargs or !$checkniletcargs)) {
for (@types) {
if(ApplyHeaders("extern $_ $function(". ($checknilargs?"...":"").");main(){}",[@headers])) {
$rettype = $_; # Great, we found the return type.
last;
}
}
}
if(!defined($rettype) and $checkreturnnil) {
die "No way to deduce function prototype in a rational amount of time";
}
my $numargs=-1;
my $varargs=0;
for (0..32) {
if(ApplyHeaders("main(){ $function(".join(",",("0") x $_).");}",@headers)) {
$numargs=$_;
if(ApplyHeaders("main(){ $function(".join(",",("0") x ($_+1)).");}",@headers)) {
$varargs=1;
}
last
}
}
die "Unable to deduce number of arguments" if $numargs==-1;
if($varargs) { $args[$numargs]="..."; }
# OK, now we know how many arguments the thing takes.
if(@args>0 and !defined($rettype)) {
for (@types) {
if(defined(ApplyHeaders("extern $_ $function(".join(",",@args).");main(){}",[@headers]))) {
$rettype = $_; # Great, we found the return type.
last;
}
}
}
print "Return type: $rettype\nArguments: ",join(",",@args),"\n";
print "Valid number of arguments: $numargs\n";
print "Accepts variable number of args: $varargs\n";
}
#$Verbose=1;
#print scalar(join("|",CheckHeader("sgtty.h"))),"\n";
#print scalar(join("|",FindHeader(CheckHeader("sgtty.h")))),"\n";
#print scalar(join("|",CheckSymbol("COLOR_PAIRS","curses.h","-lcurses","ncurses.h","-lncurses","ncurses/ncurses.h","ncurses/libncurses.a"))),"\n";
#print scalar(join("|",GetNumericSymbol("PRIO_USER","sys/resource.h",""))),"\n";
|