1# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/ 2# All rights reserved 3 4# @synopsis: 5# 6# The 'cc' module supports checking various 'features' of the C or C++ 7# compiler/linker environment. Common commands are cc-check-includes, 8# cc-check-types, cc-check-functions, cc-with, make-autoconf-h and make-template. 9# 10# The following environment variables are used if set: 11# 12## CC - C compiler 13## CXX - C++ compiler 14## CCACHE - Set to "none" to disable automatic use of ccache 15## CFLAGS - Additional C compiler flags 16## CXXFLAGS - Additional C++ compiler flags 17## LDFLAGS - Additional compiler flags during linking 18## LIBS - Additional libraries to use (for all tests) 19## CROSS - Tool prefix for cross compilation 20# 21# The following variables are defined from the corresponding 22# environment variables if set. 23# 24## CPPFLAGS 25## LINKFLAGS 26## CC_FOR_BUILD 27## LD 28 29use system 30 31module-options {} 32 33# Note that the return code is not meaningful 34proc cc-check-something {name code} { 35 uplevel 1 $code 36} 37 38# Checks for the existence of the given function by linking 39# 40proc cctest_function {function} { 41 cctest -link 1 -declare "extern void $function\(void);" -code "$function\();" 42} 43 44# Checks for the existence of the given type by compiling 45proc cctest_type {type} { 46 cctest -code "$type _x;" 47} 48 49# Checks for the existence of the given type/structure member. 50# e.g. "struct stat.st_mtime" 51proc cctest_member {struct_member} { 52 lassign [split $struct_member .] struct member 53 cctest -code "static $struct _s; return sizeof(_s.$member);" 54} 55 56# Checks for the existence of the given define by compiling 57# 58proc cctest_define {name} { 59 cctest -code "#ifndef $name\n#error not defined\n#endif" 60} 61 62# Checks for the existence of the given name either as 63# a macro (#define) or an rvalue (such as an enum) 64# 65proc cctest_decl {name} { 66 cctest -code "#ifndef $name\n(void)$name;\n#endif" 67} 68 69# @cc-check-sizeof type ... 70# 71# Checks the size of the given types (between 1 and 32, inclusive). 72# Defines a variable with the size determined, or "unknown" otherwise. 73# e.g. for type 'long long', defines SIZEOF_LONG_LONG. 74# Returns the size of the last type. 75# 76proc cc-check-sizeof {args} { 77 foreach type $args { 78 msg-checking "Checking for sizeof $type..." 79 set size unknown 80 # Try the most common sizes first 81 foreach i {4 8 1 2 16 32} { 82 if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} { 83 set size $i 84 break 85 } 86 } 87 msg-result $size 88 set define [feature-define-name $type SIZEOF_] 89 define $define $size 90 } 91 # Return the last result 92 get-define $define 93} 94 95# Checks for each feature in $list by using the given script. 96# 97# When the script is evaluated, $each is set to the feature 98# being checked, and $extra is set to any additional cctest args. 99# 100# Returns 1 if all features were found, or 0 otherwise. 101proc cc-check-some-feature {list script} { 102 set ret 1 103 foreach each $list { 104 if {![check-feature $each $script]} { 105 set ret 0 106 } 107 } 108 return $ret 109} 110 111# @cc-check-includes includes ... 112# 113# Checks that the given include files can be used 114proc cc-check-includes {args} { 115 cc-check-some-feature $args { 116 cctest -includes $each 117 } 118} 119 120# @cc-check-types type ... 121# 122# Checks that the types exist. 123proc cc-check-types {args} { 124 cc-check-some-feature $args { 125 cctest_type $each 126 } 127} 128 129# @cc-check-defines define ... 130# 131# Checks that the given preprocessor symbol is defined 132proc cc-check-defines {args} { 133 cc-check-some-feature $args { 134 cctest_define $each 135 } 136} 137 138# @cc-check-decls name ... 139# 140# Checks that each given name is either a preprocessor symbol or rvalue 141# such as an enum. Note that the define used for a decl is HAVE_DECL_xxx 142# rather than HAVE_xxx 143proc cc-check-decls {args} { 144 set ret 1 145 foreach name $args { 146 msg-checking "Checking for $name..." 147 set r [cctest_decl $name] 148 define-feature "decl $name" $r 149 if {$r} { 150 msg-result "ok" 151 } else { 152 msg-result "not found" 153 set ret 0 154 } 155 } 156 return $ret 157} 158 159# @cc-check-functions function ... 160# 161# Checks that the given functions exist (can be linked) 162proc cc-check-functions {args} { 163 cc-check-some-feature $args { 164 cctest_function $each 165 } 166} 167 168# @cc-check-members type.member ... 169# 170# Checks that the given type/structure members exist. 171# A structure member is of the form "struct stat.st_mtime" 172proc cc-check-members {args} { 173 cc-check-some-feature $args { 174 cctest_member $each 175 } 176} 177 178# @cc-check-function-in-lib function libs ?otherlibs? 179# 180# Checks that the given given function can be found in one of the libs. 181# 182# First checks for no library required, then checks each of the libraries 183# in turn. 184# 185# If the function is found, the feature is defined and lib_$function is defined 186# to -l$lib where the function was found, or "" if no library required. 187# In addition, -l$lib is added to the LIBS define. 188# 189# If additional libraries may be needed for linking, they should be specified 190# as $extralibs as "-lotherlib1 -lotherlib2". 191# These libraries are not automatically added to LIBS. 192# 193# Returns 1 if found or 0 if not. 194# 195proc cc-check-function-in-lib {function libs {otherlibs {}}} { 196 msg-checking "Checking libs for $function..." 197 set found 0 198 cc-with [list -libs $otherlibs] { 199 if {[cctest_function $function]} { 200 msg-result "none needed" 201 define lib_$function "" 202 incr found 203 } else { 204 foreach lib $libs { 205 cc-with [list -libs -l$lib] { 206 if {[cctest_function $function]} { 207 msg-result -l$lib 208 define lib_$function -l$lib 209 define-append LIBS -l$lib 210 incr found 211 break 212 } 213 } 214 } 215 } 216 } 217 if {$found} { 218 define [feature-define-name $function] 219 } else { 220 msg-result "no" 221 } 222 return $found 223} 224 225# @cc-check-tools tool ... 226# 227# Checks for existence of the given compiler tools, taking 228# into account any cross compilation prefix. 229# 230# For example, when checking for "ar", first AR is checked on the command 231# line and then in the environment. If not found, "${host}-ar" or 232# simply "ar" is assumed depending upon whether cross compiling. 233# The path is searched for this executable, and if found AR is defined 234# to the executable name. 235# 236# It is an error if the executable is not found. 237# 238proc cc-check-tools {args} { 239 foreach tool $args { 240 set TOOL [string toupper $tool] 241 set exe [get-env $TOOL [get-define cross]$tool] 242 if {![find-executable $exe]} { 243 user-error "Failed to find $exe" 244 } 245 define $TOOL $exe 246 } 247} 248 249# @cc-check-progs prog ... 250# 251# Checks for existence of the given executables on the path. 252# 253# For example, when checking for "grep", the path is searched for 254# the executable, 'grep', and if found GREP is defined as "grep". 255# 256# It the executable is not found, the variable is defined as false. 257# Returns 1 if all programs were found, or 0 otherwise. 258# 259proc cc-check-progs {args} { 260 set failed 0 261 foreach prog $args { 262 set PROG [string toupper $prog] 263 msg-checking "Checking for $prog..." 264 if {![find-executable $prog]} { 265 msg-result no 266 define $PROG false 267 incr failed 268 } else { 269 msg-result ok 270 define $PROG $prog 271 } 272 } 273 expr {!$failed} 274} 275 276# Adds the given settings to $::autosetup(ccsettings) and 277# returns the old settings. 278# 279proc cc-add-settings {settings} { 280 if {[llength $settings] % 2} { 281 autosetup-error "settings list is missing a value: $settings" 282 } 283 284 set prev [cc-get-settings] 285 # workaround a bug in some versions of jimsh by forcing 286 # conversion of $prev to a list 287 llength $prev 288 289 array set new $prev 290 291 foreach {name value} $settings { 292 switch -exact -- $name { 293 -cflags - -includes { 294 # These are given as lists 295 lappend new($name) {*}$value 296 } 297 -declare { 298 lappend new($name) $value 299 } 300 -libs { 301 # Note that new libraries are added before previous libraries 302 set new($name) [list {*}$value {*}$new($name)] 303 } 304 -link - -lang { 305 set new($name) $value 306 } 307 -source - -sourcefile - -code { 308 # XXX: These probably are only valid directly from cctest 309 set new($name) $value 310 } 311 default { 312 autosetup-error "unknown cctest setting: $name" 313 } 314 } 315 } 316 317 cc-store-settings [array get new] 318 319 return $prev 320} 321 322proc cc-store-settings {new} { 323 set ::autosetup(ccsettings) $new 324} 325 326proc cc-get-settings {} { 327 return $::autosetup(ccsettings) 328} 329 330# Similar to cc-add-settings, but each given setting 331# simply replaces the existing value. 332# 333# Returns the previous settings 334proc cc-update-settings {args} { 335 set prev [cc-get-settings] 336 cc-store-settings [dict merge $prev $args] 337 return $prev 338} 339 340# @cc-with settings ?{ script }? 341# 342# Sets the given 'cctest' settings and then runs the tests in 'script'. 343# Note that settings such as -lang replace the current setting, while 344# those such as -includes are appended to the existing setting. 345# 346# If no script is given, the settings become the default for the remainder 347# of the auto.def file. 348# 349## cc-with {-lang c++} { 350## # This will check with the C++ compiler 351## cc-check-types bool 352## cc-with {-includes signal.h} { 353## # This will check with the C++ compiler, signal.h and any existing includes. 354## ... 355## } 356## # back to just the C++ compiler 357## } 358# 359# The -libs setting is special in that newer values are added *before* earlier ones. 360# 361## cc-with {-libs {-lc -lm}} { 362## cc-with {-libs -ldl} { 363## cctest -libs -lsocket ... 364## # libs will be in this order: -lsocket -ldl -lc -lm 365## } 366## } 367proc cc-with {settings args} { 368 if {[llength $args] == 0} { 369 cc-add-settings $settings 370 } elseif {[llength $args] > 1} { 371 autosetup-error "usage: cc-with settings ?script?" 372 } else { 373 set save [cc-add-settings $settings] 374 set rc [catch {uplevel 1 [lindex $args 0]} result info] 375 cc-store-settings $save 376 if {$rc != 0} { 377 return $result -code [dict get $info -code] 378 } 379 return $result 380 } 381} 382 383# @cctest ?settings? 384# 385# Low level C compiler checker. Compiles and or links a small C program 386# according to the arguments and returns 1 if OK, or 0 if not. 387# 388# Supported settings are: 389# 390## -cflags cflags A list of flags to pass to the compiler 391## -includes list A list of includes, e.g. {stdlib.h stdio.h} 392## -declare code Code to declare before main() 393## -link 1 Don't just compile, link too 394## -lang c|c++ Use the C (default) or C++ compiler 395## -libs liblist List of libraries to link, e.g. {-ldl -lm} 396## -code code Code to compile in the body of main() 397## -source code Compile a complete program. Ignore -includes, -declare and -code 398## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file] 399# 400# Unless -source or -sourcefile is specified, the C program looks like: 401# 402## #include <firstinclude> /* same for remaining includes in the list */ 403## 404## declare-code /* any code in -declare, verbatim */ 405## 406## int main(void) { 407## code /* any code in -code, verbatim */ 408## return 0; 409## } 410# 411# Any failures are recorded in 'config.log' 412# 413proc cctest {args} { 414 set src conftest__.c 415 set tmp conftest__ 416 417 # Easiest way to merge in the settings 418 cc-with $args { 419 array set opts [cc-get-settings] 420 } 421 422 if {[info exists opts(-sourcefile)]} { 423 set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"] 424 } 425 if {[info exists opts(-source)]} { 426 set lines $opts(-source) 427 } else { 428 foreach i $opts(-includes) { 429 if {$opts(-code) ne "" && ![feature-checked $i]} { 430 # Compiling real code with an unchecked header file 431 # Quickly (and silently) check for it now 432 433 # Remove all -includes from settings before checking 434 set saveopts [cc-update-settings -includes {}] 435 msg-quiet cc-check-includes $i 436 cc-store-settings $saveopts 437 } 438 if {$opts(-code) eq "" || [have-feature $i]} { 439 lappend source "#include <$i>" 440 } 441 } 442 lappend source {*}$opts(-declare) 443 lappend source "int main(void) {" 444 lappend source $opts(-code) 445 lappend source "return 0;" 446 lappend source "}" 447 448 set lines [join $source \n] 449 } 450 451 # Build the command line 452 set cmdline {} 453 lappend cmdline {*}[get-define CCACHE] 454 switch -exact -- $opts(-lang) { 455 c++ { 456 lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS] 457 } 458 c { 459 lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS] 460 } 461 default { 462 autosetup-error "cctest called with unknown language: $opts(-lang)" 463 } 464 } 465 466 if {!$opts(-link)} { 467 set tmp conftest__.o 468 lappend cmdline -c 469 } 470 lappend cmdline {*}$opts(-cflags) 471 472 switch -glob -- [get-define host] { 473 *-*-darwin* { 474 # Don't generate .dSYM directories 475 lappend cmdline -gstabs 476 } 477 } 478 lappend cmdline $src -o $tmp {*}$opts(-libs) 479 480 # At this point we have the complete command line and the 481 # complete source to be compiled. Get the result from cache if 482 # we can 483 if {[info exists ::cc_cache($cmdline,$lines)]} { 484 msg-checking "(cached) " 485 set ok $::cc_cache($cmdline,$lines) 486 if {$::autosetup(debug)} { 487 configlog "From cache (ok=$ok): [join $cmdline]" 488 configlog "============" 489 configlog $lines 490 configlog "============" 491 } 492 return $ok 493 } 494 495 writefile $src $lines\n 496 497 set ok 1 498 if {[catch {exec-with-stderr {*}$cmdline} result errinfo]} { 499 configlog "Failed: [join $cmdline]" 500 configlog $result 501 configlog "============" 502 configlog "The failed code was:" 503 configlog $lines 504 configlog "============" 505 set ok 0 506 } elseif {$::autosetup(debug)} { 507 configlog "Compiled OK: [join $cmdline]" 508 configlog "============" 509 configlog $lines 510 configlog "============" 511 } 512 file delete $src 513 file delete $tmp 514 515 # cache it 516 set ::cc_cache($cmdline,$lines) $ok 517 518 return $ok 519} 520 521# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*? 522# 523# Deprecated - see make-config-header 524proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} { 525 user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead" 526 make-config-header $file -auto $autopatterns -bare $barepatterns 527} 528 529# @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ... 530# 531# Examines all defined variables which match the given patterns 532# and writes an include file, $file, which defines each of these. 533# Variables which match '-auto' are output as follows: 534# - defines which have the value "0" are ignored. 535# - defines which have integer values are defined as the integer value. 536# - any other value is defined as a string, e.g. "value" 537# Variables which match '-bare' are defined as-is. 538# Variables which match '-str' are defined as a string, e.g. "value" 539# Variables which match '-none' are omitted. 540# 541# Note that order is important. The first pattern which matches is selected 542# Default behaviour is: 543# 544# -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none * 545# 546# If the file would be unchanged, it is not written. 547proc make-config-header {file args} { 548 set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]] 549 file mkdir [file dirname $file] 550 set lines {} 551 lappend lines "#ifndef $guard" 552 lappend lines "#define $guard" 553 554 # Add some defaults 555 lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* 556 557 foreach n [lsort [dict keys [all-defines]]] { 558 set value [get-define $n] 559 set type [calc-define-output-type $n $args] 560 switch -exact -- $type { 561 -bare { 562 # Just output the value unchanged 563 } 564 -none { 565 continue 566 } 567 -str { 568 set value \"$value\" 569 } 570 -auto { 571 # Automatically determine the type 572 if {$value eq "0"} { 573 lappend lines "/* #undef $n */" 574 continue 575 } 576 if {![string is integer -strict $value]} { 577 set value \"$value\" 578 } 579 } 580 "" { 581 continue 582 } 583 default { 584 autosetup-error "Unknown type in make-config-header: $type" 585 } 586 } 587 lappend lines "#define $n $value" 588 } 589 lappend lines "#endif" 590 set buf [join $lines \n] 591 write-if-changed $file $buf { 592 msg-result "Created $file" 593 } 594} 595 596proc calc-define-output-type {name spec} { 597 foreach {type patterns} $spec { 598 foreach pattern $patterns { 599 if {[string match $pattern $name]} { 600 return $type 601 } 602 } 603 } 604 return "" 605} 606 607# Initialise some values from the environment or commandline or default settings 608foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} { 609 lassign $i var default 610 define $var [get-env $var $default] 611} 612 613if {[env-is-set CC]} { 614 # Set by the user, so don't try anything else 615 set try [list [get-env CC ""]] 616} else { 617 # Try some reasonable options 618 set try [list [get-define cross]cc [get-define cross]gcc] 619} 620define CC [find-an-executable {*}$try] 621if {[get-define CC] eq ""} { 622 user-error "Could not find a C compiler. Tried: [join $try ", "]" 623} 624 625define CPP [get-env CPP "[get-define CC] -E"] 626 627# XXX: Could avoid looking for a C++ compiler until requested 628# Note that if CXX isn't found, we just set it to "false". It might not be needed. 629if {[env-is-set CXX]} { 630 define CXX [find-an-executable -required [get-env CXX ""]] 631} else { 632 define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false] 633} 634 635# CXXFLAGS default to CFLAGS if not specified 636define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]] 637 638cc-check-tools ld 639 640# May need a CC_FOR_BUILD, so look for one 641define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false] 642 643if {[get-define CC] eq ""} { 644 user-error "Could not find a C compiler. Tried: [join $try ", "]" 645} 646 647define CCACHE [find-an-executable [get-env CCACHE ccache]] 648 649# Initial cctest settings 650cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {}} 651 652msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]" 653if {[get-define CXX] ne "false"} { 654 msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]" 655} 656msg-result "Build C compiler...[get-define CC_FOR_BUILD]" 657 658if {![cc-check-includes stdlib.h]} { 659 user-error "Compiler does not work. See config.log" 660} 661