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