crossroads

Git mirror of https://crossroads.e-tunity.com/
git clone git://git.finwo.net/app/crossroads
Log | Files | Refs

c-conf (12233B)


      1 #!/usr/bin/perl
      2 
      3 use strict;
      4 use Getopt::Std;
      5 
      6 # Globals
      7 my $VER = "1.05";
      8 # 1.05 [KK 2006-09-28] Flag -s (silent) implemented. Usage text updated.
      9 # 1.04 [KK 2006-09-05] C-compilers: gcc/g++ get selected first, instead of
     10 #			cc/c++. Helps HP-UX ports. [Thanks, Bernd Krumboeck.]
     11 # 1.03 [KK 2006-07-19] 'subfiles' keeps track of visited dirs incase of
     12 #		       recursion. Testing is now by inode, used to be by name.
     13 # 1.02 [KK 2006-06-01] 'findbin' searches for .exe too now, for Cygwin support
     14 # 1.01 [KK 2005-09-29] Implemented context-sensitive help via -h.
     15 #		       Action 'header' implemented.
     16 # 1.00 [KK 2005-09-28] First version
     17 
     18 # Configuration
     19 my @def_headerdirs = ('/usr/include',
     20 		      '/usr/local/include',
     21 		      "$ENV{HOME}/include",
     22 		     );
     23 my @def_libdirs = ('/usr/lib',
     24 		   '/usr/local/lib',
     25 		   '/usr/ucblib',
     26 		   "$ENV{HOME}/lib",
     27 		  );
     28 my @c_compilers = ('gcc', 'cc');
     29 my @cpp_compilers = ('g++', 'c++');
     30 
     31 # Globals
     32 my %opts;
     33 my $base;
     34 my @warnings;
     35 my $printed;
     36 my @headerdirs;
     37 my @libdirs;
     38 
     39 # Show usage and croak
     40 sub usage {
     41     die <<"ENDUSAGE"
     42 
     43 This is c-conf, the C compilation configuration helper V$VER
     44 Copyright (c) e-tunity. Contact <info\@e-tunity.com> for information.
     45 
     46 Usage:
     47     $base [flags] header FILE.H [FILE.H...] Searches for directories
     48 	containing the named header(s), returns appropriate -I flags.
     49     $base [flags] headerdir DIR [DIR...]: Searches for directory
     50 	containing headers, returns appropriate -I flags.
     51     $base [flags] ifheader FILE.H DEFINE Searches for the named header.
     52 	If found, a compilation flag -DDEFINE is returned, indicating that
     53 	the header is found.
     54     $base [flags] libfunction FUNC DEFINE Creates a small program that
     55 	tries to use FUNC. If this succeeds, a -DDEFINE=1 flag is returned.
     56     $base [flags] lib NAME [NAME...]: Searches for libNAME.{a,so,...},
     57 	returns appropriate -L and -l flags.
     58     $base [flags] so-name NAME: Returns filename of a shared-object for NAME,
     59         e.g. libNAME.so
     60     $base [flags] so-cflags: Returns compilation flags to build shared
     61         objects
     62     $base [flags] so-lflags: Returns linkage flags to produce a shared-object
     63 	library
     64     $base [flags] c-compiler: Returns name of C compiler
     65     $base [flags] c++-compiler: Returns name of C++ compiler
     66 
     67 Optional flags:
     68     -h: to show short help for an action, e.g. try '$base -h so-name'
     69     -s: to suppress showing of warnings
     70     -v: to show verbose messages
     71     -I DIR[,DIR..]: to add DIR(s) to the searchpath for headers, default
     72 	searchpath is @headerdirs
     73     -L DIR[,DIR..]: to add DIR(s) to the searchpath for libraries, default
     74 	searchpath is @libdirs
     75 
     76 Meaningful output is returned on stdout. Verbose messages, warnings and
     77 errors go to stderr.
     78 
     79 ENDUSAGE
     80 }
     81 
     82 # Issue a warning
     83 sub warning {
     84     push (@warnings, "@_");
     85 }
     86 
     87 # Show a message
     88 sub msg {
     89     return unless ($opts{v});
     90     print STDERR ("$base: ", @_);
     91 }
     92 
     93 # Show help info if -h was given
     94 sub checkhelp {
     95     return unless ($opts{h});
     96     print STDERR (@_);
     97     exit (1);
     98 }
     99 
    100 # Basename / dirname of a file.
    101 sub basename ($) {
    102     my $name = shift;
    103     $name =~ s{.*/}{};
    104     return ($name);
    105 }
    106 sub dirname ($) {
    107     my $name = shift;
    108     return (undef) unless ($name =~ /\//);
    109     $name =~ s{/[^/]$}{};
    110     return ($name);
    111 }
    112 
    113 # Get the uname.
    114 sub uname() {
    115     my $ret = `uname`;
    116     chomp ($ret);
    117     msg ("uname: $ret\n");
    118     return ($ret);
    119 }
    120 
    121 # Find a binary along the path.
    122 sub findbin($) {
    123     my $bin = shift;
    124     msg ("Looking for executable '$bin'\n");
    125     foreach my $d (split (/:/, $ENV{PATH})) {
    126 	if (-x "$d/$bin" or -f "$d/bin.exe") {
    127 	    msg ("Found as '$d/$bin'\n");
    128 	    return ("$d/$bin");
    129 	}
    130     }
    131     msg ("Not found!\n");
    132 }
    133 
    134 # Recursively determine the files under a given dir.
    135 my %_dir_visited;
    136 sub subfiles ($$$) {
    137     my ($dir, $mask, $recursive) = @_;
    138 
    139     %_dir_visited = () unless ($recursive);
    140     my ($dev, $ino) = stat($dir)
    141       or return (undef);
    142     my $tag = sprintf ("%d-%d", $dev, $ino);
    143     if ($_dir_visited{$tag}) {
    144 	msg ("Path '$dir' was already visited (as $_dir_visited{$tag})\n");
    145 	return (undef);
    146     }
    147     $_dir_visited{$tag} = $dir;
    148 
    149     msg ("Scanning for '$mask' under '$dir'\n");
    150     if (! -d $dir) {
    151 	msg ("Scan path ends, '$dir' is not an accessible directory\n");
    152 	return (undef);
    153     }
    154 
    155     my @ret = ();
    156     foreach my $f (glob ("$dir/$mask")) {
    157 	if (-f $f) {
    158 	    push (@ret, $f);
    159 	    msg ("Found a hit as '$f'\n");
    160 	}
    161 	msg ("Hits so far: ", $#ret + 1, "\n") if ($#ret > -1);
    162     }
    163     foreach my $d (glob ("$dir/*")) {
    164 	next unless (-d $d);
    165 	msg ("Recursing from '$dir' into '$d'\n");
    166 	my @subret = subfiles ("$d", $mask, 1);
    167 	my $added = 0;
    168 	foreach my $f (@subret) {
    169 	    if (-f $f) {
    170 		push (@ret, $f);
    171 		$added++;
    172 	    }
    173 	}
    174 	msg ("Added ", $added, " hits from '$d'\n") if ($added);
    175     }
    176     if ($#ret > -1) {
    177 	msg ("Found ", $#ret + 1, " entries matching '$mask' under '$dir'\n");
    178 	return (@ret);
    179     } else {
    180 	# msg ("No entries matching '$mask' under '$dir' found\n");
    181 	return (undef);
    182     }
    183 }
    184 
    185 # Output stuff
    186 sub output {
    187     print (' ') if ($printed++);
    188     print (@_);
    189 }
    190 
    191 # Find a header, output a define if found.
    192 sub if_header {
    193     checkhelp <<"ENDHELP";
    194 'ifheader' tries to find a header file in the 'include' directories.
    195 When found, a define-flag for the C compiler is returned.
    196 E.g.: $base ifheader malloc.h HAVE_MALLOC_H (may return -DHAVE_MALLOC_H)
    197 Use in a Makefile as in:
    198 CFLAGS = \$(CFLAGS) \$(shell c-conf ifheader malloc.h HAVE_MALLOC_H
    199 Then in a C source as:
    200 #ifdef HAVE_MALLOC_H
    201 #include <malloc.h>
    202 #endif
    203 ENDHELP
    204 
    205     usage() if ($#_ != 1);
    206 
    207     my ($h, $def) = @_;
    208     msg ("Looking for '$h'\n");
    209 
    210     foreach my $d (@headerdirs) {
    211 	if (-f "$d/$h") {
    212 	    output ("-D$def");
    213 	    return;
    214 	}
    215     }
    216 }
    217 
    218 # Find a header
    219 sub header {
    220     checkhelp <<"ENDHELP";
    221 'header' locates one or more C headers in the 'include' directories.
    222 E.g.: $base header e-lib.h stdio.h (may return -I/usr/include -I/usr/e/include)
    223 Use in a Makefile as in:
    224 CFLAGS = -C -Wall \$(shell c-conf header e-lib.h)
    225 Then in a C source as:
    226 #include <e-lib.h>
    227 ENDHELP
    228 
    229     usage() if ($#_ == -1);
    230     foreach my $h (@_) {
    231 	msg ("Looking for '$h'\n");
    232 	my $found = 0;
    233 	foreach my $d (@headerdirs) {
    234 	    msg ("Trying '$d/$h'\n");
    235 	    if (-f "$d/$h") {
    236 		$found++;
    237 		msg ("Found\n");
    238 		output ("-I$d");
    239 		last;
    240 	    }
    241 	}
    242 	warning ("Failed to locate header '$h' in @headerdirs\n")
    243 	  unless ($found);
    244     }
    245 }
    246 
    247 # Find a header directory
    248 sub headerdir {
    249     checkhelp <<"ENDHELP";
    250 'headerdir' locates directories under which (in steps) C headers are
    251 E.g.: $base headerdir libxml2 (may return '-I/usr/include/libxml2')
    252 Use in a Makefile as in:
    253 CFLAGS = -C -Wall \$(shell c-conf headerdir libxml2)
    254 Then in a C source as:
    255 #include <libxml/xpath.h>
    256 ENDHELP
    257 
    258     usage() if ($#_ == -1);
    259     foreach my $headerdir (@_) {
    260 	msg ("Looking for header dir '$headerdir'\n");
    261 	my $found = 0;
    262 	foreach my $d (@headerdirs) {
    263 	    msg ("Trying as '$d/$headerdir'\n");
    264 	    my $target = "$d/$headerdir";
    265 	    if (subfiles ($target, '*.h', 0)) {
    266 		output ("-I$target");
    267 		$found++;
    268 	    }
    269 	}
    270 	warning ("Header dir '$headerdir' not found\n")
    271 	  unless ($found);
    272     }
    273 }
    274 
    275 # Find a library
    276 sub lib {
    277     checkhelp <<"ENDHELP";
    278 'lib' generates the linkage flags for a given library name. The name
    279 is bare, without 'lib' and '.so' and the like.
    280 E.g.: $base lib xml2 (may return '-L/usr/lib -lxml2')
    281 Use in a Makefile as in:
    282 LDFLAGS = \$(shell c-conf lib xml2)
    283 ENDHELP
    284 
    285     usage() if ($#_ == -1);
    286     foreach my $lib (@_) {
    287 	msg ("Looking for lib '$lib'\n");
    288 	my $found = 0;
    289 	foreach my $d (@libdirs) {
    290 	    msg ("Trying under '$d'\n");
    291 	    my $hit = (subfiles ($d, "lib$lib.*", 0))[0];
    292 	    if ($hit) {
    293 		msg ("Found as '$hit'\n");
    294 		$found++;
    295 		$hit =~ s{/[^/]*$}{};
    296 		output ("-L$hit -l$lib");
    297 	    }
    298 	}
    299 	warning ("Library '$lib' not found\n")
    300 	  unless ($found);
    301     }
    302 }
    303 
    304 # Compilation flags to make a so-ready object.
    305 sub so_cflags {
    306     checkhelp <<"ENDHELP";
    307 'so-cflags' returns the compilation flags that are necessary when
    308 building objects for a shared library.
    309 E.g.: $base so-cflags (may return '-fPIC')
    310 Use in a Makefile as in:
    311 CFLAGS = -c -g -Wall \$(shell c-conf so-cflags)
    312 ENDHELP
    313 
    314     usage() if ($#_ > -1);
    315     if (uname() eq 'Darwin') {
    316 	output ('-fPIC');
    317     } elsif (uname() eq 'Linux') {
    318 	output ('-fpic');
    319     }
    320 }
    321 
    322 # Linkage flags to make an so.
    323 sub so_lflags {
    324     checkhelp << "ENDHELP";
    325 'so-lflags' returns the linkage flags that are necessary when
    326 combining objects into a shared library.
    327 E.g.: $base so-lflags (may return '-dynamiclib -Wl,-single_module')
    328 Use in a Makefile as in:
    329 MY_SO = \$(shell c-conf so-name my)
    330 \$(MY_SO): *.o
    331 	\$(CC) -o \$(MY_SO) \$(shell c-conf so-lflags) *.o
    332 ENDHELP
    333 
    334     usage() if ($#_ > -1);
    335     my $lib = shift;
    336 
    337     if (uname() eq 'Darwin') {
    338 	output ("-dynamiclib -Wl,-single_module");
    339     } else {
    340 	output ("-shared");
    341     }
    342 }
    343 
    344 # Get the C compiler
    345 sub c_compiler {
    346     checkhelp <<"ENDHELP";
    347 'c-compiler' tries to find a C compiler and returns its (bare) name.
    348 E.g.: $base c-compiler
    349       -> gcc
    350 ENDHELP
    351 
    352     usage() if ($#_ > -1);
    353     foreach my $c (@c_compilers) {
    354 	if (findbin ($c)) {
    355 	    output ($c);
    356 	    return;
    357 	}
    358     }
    359     warning ("No C compiler found\n");
    360 }
    361 
    362 # Get the C++ compiler
    363 sub cpp_compiler {
    364     checkhelp <<"ENDHELP";
    365 'c++-compiler' tries to find a C++ compiler and returns its (bare) name.
    366 E.g.: $base c++-compiler
    367       -> g++
    368 ENDHELP
    369 
    370     usage() if ($#_ > -1);
    371     foreach my $c (@cpp_compilers) {
    372 	if (findbin ($c)) {
    373 	    output ($c);
    374 	    return;
    375 	}
    376     }
    377     warning ("No C++ compiler found\n");
    378 }
    379 
    380 # Get the name for an SO.
    381 sub so_name {
    382     checkhelp <<"ENDHELP";
    383 'so-name' returns the filename of a shared library, based on the LIB
    384 argument.
    385 E.g.: $base so-name test
    386       -> libtest.so
    387 ENDHELP
    388 
    389     usage() if ($#_ != 0);
    390     my $name = shift;
    391 
    392     my $dir  = dirname ($name);
    393     my $base = basename ($name);
    394 
    395     my $dest;
    396     if (uname() eq 'Darwin') {
    397 	$dest = "lib$base.dylib";
    398     } else {
    399 	$dest = "lib$base.so";
    400     }
    401 
    402     if ($dir ne '') {
    403 	output ("$dir/$dest");
    404     } else {
    405 	output ("$dest");
    406     }
    407 }
    408 
    409 # Check that a libfunction is present.
    410 sub libfunction {
    411     checkhelp <<"ENDHELP";
    412 'libfunction' checks whether a library function is present. There are
    413 two arguments: the function to check, and a define to output when the
    414 function is found. The output is a -D flag for the compiler commandline.
    415 E.g.: $base libfunction printf HAVE_PRINTF
    416       -> -DHAVE_PRINTF=1
    417       $base libfunction foo_bar HAVE_FOOBAR
    418       -> (nothing)      
    419 ENDHELP
    420 
    421     usage() if ($#_ != 1);
    422     my ($func, $def) = @_;
    423 
    424     # Create a temp .c file.
    425     my $src = "/tmp/$$.c";
    426     my $dst = "/tmp/$$.out";
    427     open (my $of, ">$src")
    428       or die ("Cannot write $src: $!\n");
    429     print $of ("main () {\n",
    430 	       "    void $func (void);\n",
    431 	       "    $func();\n",
    432 	       "}\n");
    433     close ($of);
    434 
    435     my $cc;
    436     foreach my $c (@c_compilers) {
    437 	if (findbin ($c)) {
    438 	    $cc = $c;
    439 	    last;
    440 	}
    441     }
    442     die ("Failed to locate C compiler\n") if ($cc eq '');
    443     my $ret = system ("$cc -o $dst $src >/dev/null 2>&1");
    444     unlink ($src, $dst);
    445 
    446     output ("-D$def=1") if ($ret == 0);
    447 }
    448 
    449 # Main starts here
    450 
    451 $base = $0;
    452 $base =~ s{.*/}{};
    453 usage () unless (getopts ('vhI:L:s', \%opts));
    454 foreach my $d (split (/,/, $opts{L})) {
    455     push (@libdirs, $d);
    456 }
    457 foreach my $d (split (/,/, $opts{I})) {
    458     push (@headerdirs, $d);
    459 }    
    460 
    461 push (@libdirs, @def_libdirs);
    462 push (@headerdirs, @def_headerdirs);
    463 my $action = shift (@ARGV);
    464 
    465 if ($action eq 'header') {
    466     header (@ARGV);
    467 } elsif ($action eq 'headerdir') {
    468     headerdir (@ARGV);
    469 } elsif ($action eq 'lib') {
    470     lib (@ARGV);
    471 } elsif ($action eq 'so-cflags') {
    472     so_cflags (@ARGV);
    473 } elsif ($action eq 'so-lflags') {
    474     so_lflags (@ARGV);
    475 } elsif ($action eq 'c-compiler') {
    476     c_compiler(@ARGV);
    477 } elsif ($action eq 'c++-compiler') {
    478     cpp_compiler(@ARGV);
    479 } elsif ($action eq 'so-name') {
    480     so_name (@ARGV);
    481 } elsif ($action eq 'ifheader') {
    482     if_header (@ARGV);
    483 } elsif ($action eq 'libfunction') {
    484     libfunction (@ARGV);
    485 } else {
    486     usage ();
    487 }
    488 
    489 print ("\n") if ($printed);
    490 if ($#warnings > -1) {
    491     foreach my $w (@warnings) {
    492 	print STDERR ("$base WARNING: $w") unless ($opts{s});
    493     }
    494     exit (1);
    495 }
    496 exit (0);
    497