expr: prevent stack overflow
[jimtcl.git] / autosetup / cc.tcl
blob50a2d4452ef1db530714ab45c034b70061ed33bb
1 # Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
2 # All rights reserved
4 # @synopsis:
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-config-header' and 'make-template'.
10 # The following environment variables are used if set:
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
21 # The following variables are defined from the corresponding
22 # environment variables if set.
24 ## CPPFLAGS
25 ## LINKFLAGS
26 ## CC_FOR_BUILD
27 ## LD
29 use system
31 module-options {}
33 # Checks for the existence of the given function by linking
35 proc cctest_function {function} {
36 cctest -link 1 -declare "extern void $function\(void);" -code "$function\();"
39 # Checks for the existence of the given type by compiling
40 proc cctest_type {type} {
41 cctest -code "$type _x;"
44 # Checks for the existence of the given type/structure member.
45 # e.g. "struct stat.st_mtime"
46 proc cctest_member {struct_member} {
47 # split at the first dot
48 regexp {^([^.]+)[.](.*)$} $struct_member -> struct member
49 cctest -code "static $struct _s; return sizeof(_s.$member);"
52 # Checks for the existence of the given define by compiling
54 proc cctest_define {name} {
55 cctest -code "#ifndef $name\n#error not defined\n#endif"
58 # Checks for the existence of the given name either as
59 # a macro (#define) or an rvalue (such as an enum)
61 proc cctest_decl {name} {
62 cctest -code "#ifndef $name\n(void)$name;\n#endif"
65 # @cc-check-sizeof type ...
67 # Checks the size of the given types (between 1 and 32, inclusive).
68 # Defines a variable with the size determined, or 'unknown' otherwise.
69 # e.g. for type 'long long', defines 'SIZEOF_LONG_LONG'.
70 # Returns the size of the last type.
72 proc cc-check-sizeof {args} {
73 foreach type $args {
74 msg-checking "Checking for sizeof $type..."
75 set size unknown
76 # Try the most common sizes first
77 foreach i {4 8 1 2 16 32} {
78 if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} {
79 set size $i
80 break
83 msg-result $size
84 set define [feature-define-name $type SIZEOF_]
85 define $define $size
87 # Return the last result
88 get-define $define
91 # Checks for each feature in $list by using the given script.
93 # When the script is evaluated, $each is set to the feature
94 # being checked, and $extra is set to any additional cctest args.
96 # Returns 1 if all features were found, or 0 otherwise.
97 proc cc-check-some-feature {list script} {
98 set ret 1
99 foreach each $list {
100 if {![check-feature $each $script]} {
101 set ret 0
104 return $ret
107 # @cc-check-includes includes ...
109 # Checks that the given include files can be used.
110 proc cc-check-includes {args} {
111 cc-check-some-feature $args {
112 set with {}
113 if {[dict exists $::autosetup(cc-include-deps) $each]} {
114 set deps [dict keys [dict get $::autosetup(cc-include-deps) $each]]
115 msg-quiet cc-check-includes {*}$deps
116 foreach i $deps {
117 if {[have-feature $i]} {
118 lappend with $i
122 if {[llength $with]} {
123 cc-with [list -includes $with] {
124 cctest -includes $each
126 } else {
127 cctest -includes $each
132 # @cc-include-needs include required ...
134 # Ensures that when checking for '$include', a check is first
135 # made for each '$required' file, and if found, it is included with '#include'.
136 proc cc-include-needs {file args} {
137 foreach depfile $args {
138 dict set ::autosetup(cc-include-deps) $file $depfile 1
142 # @cc-check-types type ...
144 # Checks that the types exist.
145 proc cc-check-types {args} {
146 cc-check-some-feature $args {
147 cctest_type $each
151 # @cc-check-defines define ...
153 # Checks that the given preprocessor symbols are defined.
154 proc cc-check-defines {args} {
155 cc-check-some-feature $args {
156 cctest_define $each
160 # @cc-check-decls name ...
162 # Checks that each given name is either a preprocessor symbol or rvalue
163 # such as an enum. Note that the define used is 'HAVE_DECL_xxx'
164 # rather than 'HAVE_xxx'.
165 proc cc-check-decls {args} {
166 set ret 1
167 foreach name $args {
168 msg-checking "Checking for $name..."
169 set r [cctest_decl $name]
170 define-feature "decl $name" $r
171 if {$r} {
172 msg-result "ok"
173 } else {
174 msg-result "not found"
175 set ret 0
178 return $ret
181 # @cc-check-functions function ...
183 # Checks that the given functions exist (can be linked).
184 proc cc-check-functions {args} {
185 cc-check-some-feature $args {
186 cctest_function $each
190 # @cc-check-members type.member ...
192 # Checks that the given type/structure members exist.
193 # A structure member is of the form 'struct stat.st_mtime'.
194 proc cc-check-members {args} {
195 cc-check-some-feature $args {
196 cctest_member $each
200 # @cc-check-function-in-lib function libs ?otherlibs?
202 # Checks that the given function can be found in one of the libs.
204 # First checks for no library required, then checks each of the libraries
205 # in turn.
207 # If the function is found, the feature is defined and 'lib_$function' is defined
208 # to '-l$lib' where the function was found, or "" if no library required.
209 # In addition, '-l$lib' is prepended to the 'LIBS' define.
211 # If additional libraries may be needed for linking, they should be specified
212 # with '$extralibs' as '-lotherlib1 -lotherlib2'.
213 # These libraries are not automatically added to 'LIBS'.
215 # Returns 1 if found or 0 if not.
217 proc cc-check-function-in-lib {function libs {otherlibs {}}} {
218 msg-checking "Checking libs for $function..."
219 set found 0
220 cc-with [list -libs $otherlibs] {
221 if {[cctest_function $function]} {
222 msg-result "none needed"
223 define lib_$function ""
224 incr found
225 } else {
226 foreach lib $libs {
227 cc-with [list -libs -l$lib] {
228 if {[cctest_function $function]} {
229 msg-result -l$lib
230 define lib_$function -l$lib
231 # prepend to LIBS
232 define LIBS "-l$lib [get-define LIBS]"
233 incr found
234 break
240 define-feature $function $found
241 if {!$found} {
242 msg-result "no"
244 return $found
247 # @cc-check-tools tool ...
249 # Checks for existence of the given compiler tools, taking
250 # into account any cross compilation prefix.
252 # For example, when checking for 'ar', first 'AR' is checked on the command
253 # line and then in the environment. If not found, '${host}-ar' or
254 # simply 'ar' is assumed depending upon whether cross compiling.
255 # The path is searched for this executable, and if found 'AR' is defined
256 # to the executable name.
257 # Note that even when cross compiling, the simple 'ar' is used as a fallback,
258 # but a warning is generated. This is necessary for some toolchains.
260 # It is an error if the executable is not found.
262 proc cc-check-tools {args} {
263 foreach tool $args {
264 set TOOL [string toupper $tool]
265 set exe [get-env $TOOL [get-define cross]$tool]
266 if {[find-executable {*}$exe]} {
267 define $TOOL $exe
268 continue
270 if {[find-executable {*}$tool]} {
271 msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect"
272 define $TOOL $tool
273 continue
275 user-error "Failed to find $exe"
279 # @cc-check-progs prog ...
281 # Checks for existence of the given executables on the path.
283 # For example, when checking for 'grep', the path is searched for
284 # the executable, 'grep', and if found 'GREP' is defined as 'grep'.
286 # If the executable is not found, the variable is defined as 'false'.
287 # Returns 1 if all programs were found, or 0 otherwise.
289 proc cc-check-progs {args} {
290 set failed 0
291 foreach prog $args {
292 set PROG [string toupper $prog]
293 msg-checking "Checking for $prog..."
294 if {![find-executable $prog]} {
295 msg-result no
296 define $PROG false
297 incr failed
298 } else {
299 msg-result ok
300 define $PROG $prog
303 expr {!$failed}
306 # @cc-path-progs prog ...
308 # Like cc-check-progs, but sets the define to the full path rather
309 # than just the program name.
311 proc cc-path-progs {args} {
312 set failed 0
313 foreach prog $args {
314 set PROG [string toupper $prog]
315 msg-checking "Checking for $prog..."
316 set path [find-executable-path $prog]
317 if {$path eq ""} {
318 msg-result no
319 define $PROG false
320 incr failed
321 } else {
322 msg-result $path
323 define $PROG $path
326 expr {!$failed}
329 # Adds the given settings to $::autosetup(ccsettings) and
330 # returns the old settings.
332 proc cc-add-settings {settings} {
333 if {[llength $settings] % 2} {
334 autosetup-error "settings list is missing a value: $settings"
337 set prev [cc-get-settings]
338 # workaround a bug in some versions of jimsh by forcing
339 # conversion of $prev to a list
340 llength $prev
342 array set new $prev
344 foreach {name value} $settings {
345 switch -exact -- $name {
346 -cflags - -includes {
347 # These are given as lists
348 lappend new($name) {*}[list-non-empty $value]
350 -declare {
351 lappend new($name) $value
353 -libs {
354 # Note that new libraries are added before previous libraries
355 set new($name) [list {*}[list-non-empty $value] {*}$new($name)]
357 -link - -lang - -nooutput {
358 set new($name) $value
360 -source - -sourcefile - -code {
361 # XXX: These probably are only valid directly from cctest
362 set new($name) $value
364 default {
365 autosetup-error "unknown cctest setting: $name"
370 cc-store-settings [array get new]
372 return $prev
375 proc cc-store-settings {new} {
376 set ::autosetup(ccsettings) $new
379 proc cc-get-settings {} {
380 return $::autosetup(ccsettings)
383 # Similar to cc-add-settings, but each given setting
384 # simply replaces the existing value.
386 # Returns the previous settings
387 proc cc-update-settings {args} {
388 set prev [cc-get-settings]
389 cc-store-settings [dict merge $prev $args]
390 return $prev
393 # @cc-with settings ?{ script }?
395 # Sets the given 'cctest' settings and then runs the tests in '$script'.
396 # Note that settings such as '-lang' replace the current setting, while
397 # those such as '-includes' are appended to the existing setting.
399 # If no script is given, the settings become the default for the remainder
400 # of the 'auto.def' file.
402 ## cc-with {-lang c++} {
403 ## # This will check with the C++ compiler
404 ## cc-check-types bool
405 ## cc-with {-includes signal.h} {
406 ## # This will check with the C++ compiler, signal.h and any existing includes.
407 ## ...
408 ## }
409 ## # back to just the C++ compiler
410 ## }
412 # The '-libs' setting is special in that newer values are added *before* earlier ones.
414 ## cc-with {-libs {-lc -lm}} {
415 ## cc-with {-libs -ldl} {
416 ## cctest -libs -lsocket ...
417 ## # libs will be in this order: -lsocket -ldl -lc -lm
418 ## }
419 ## }
420 proc cc-with {settings args} {
421 if {[llength $args] == 0} {
422 cc-add-settings $settings
423 } elseif {[llength $args] > 1} {
424 autosetup-error "usage: cc-with settings ?script?"
425 } else {
426 set save [cc-add-settings $settings]
427 set rc [catch {uplevel 1 [lindex $args 0]} result info]
428 cc-store-settings $save
429 if {$rc != 0} {
430 return -code [dict get $info -code] $result
432 return $result
436 # @cctest ?settings?
438 # Low level C/C++ compiler checker. Compiles and or links a small C program
439 # according to the arguments and returns 1 if OK, or 0 if not.
441 # Supported settings are:
443 ## -cflags cflags A list of flags to pass to the compiler
444 ## -includes list A list of includes, e.g. {stdlib.h stdio.h}
445 ## -declare code Code to declare before main()
446 ## -link 1 Don't just compile, link too
447 ## -lang c|c++ Use the C (default) or C++ compiler
448 ## -libs liblist List of libraries to link, e.g. {-ldl -lm}
449 ## -code code Code to compile in the body of main()
450 ## -source code Compile a complete program. Ignore -includes, -declare and -code
451 ## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file]
452 ## -nooutput 1 Treat any compiler output (e.g. a warning) as an error
454 # Unless '-source' or '-sourcefile' is specified, the C program looks like:
456 ## #include <firstinclude> /* same for remaining includes in the list */
458 ## declare-code /* any code in -declare, verbatim */
460 ## int main(void) {
461 ## code /* any code in -code, verbatim */
462 ## return 0;
463 ## }
465 # Any failures are recorded in 'config.log'
467 proc cctest {args} {
468 set src conftest__.c
469 set tmp conftest__
471 # Easiest way to merge in the settings
472 cc-with $args {
473 array set opts [cc-get-settings]
476 if {[info exists opts(-sourcefile)]} {
477 set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"]
479 if {[info exists opts(-source)]} {
480 set lines $opts(-source)
481 } else {
482 foreach i $opts(-includes) {
483 if {$opts(-code) ne "" && ![feature-checked $i]} {
484 # Compiling real code with an unchecked header file
485 # Quickly (and silently) check for it now
487 # Remove all -includes from settings before checking
488 set saveopts [cc-update-settings -includes {}]
489 msg-quiet cc-check-includes $i
490 cc-store-settings $saveopts
492 if {$opts(-code) eq "" || [have-feature $i]} {
493 lappend source "#include <$i>"
496 lappend source {*}$opts(-declare)
497 lappend source "int main(void) {"
498 lappend source $opts(-code)
499 lappend source "return 0;"
500 lappend source "}"
502 set lines [join $source \n]
505 # Build the command line
506 set cmdline {}
507 lappend cmdline {*}[get-define CCACHE]
508 switch -exact -- $opts(-lang) {
509 c++ {
510 lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS]
513 lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS]
515 default {
516 autosetup-error "cctest called with unknown language: $opts(-lang)"
520 if {$opts(-link)} {
521 lappend cmdline {*}[get-define LDFLAGS]
522 } else {
523 set tmp conftest__.o
524 lappend cmdline -c
526 lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""]
527 lappend cmdline $src -o $tmp {*}$opts(-libs)
528 if {$opts(-link)} {
529 lappend cmdline {*}[get-define LIBS]
532 # At this point we have the complete command line and the
533 # complete source to be compiled. Get the result from cache if
534 # we can
535 if {[info exists ::cc_cache($cmdline,$lines)]} {
536 msg-checking "(cached) "
537 set ok $::cc_cache($cmdline,$lines)
538 if {$::autosetup(debug)} {
539 configlog "From cache (ok=$ok): [join $cmdline]"
540 configlog "============"
541 configlog $lines
542 configlog "============"
544 return $ok
547 writefile $src $lines\n
549 set ok 1
550 set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
551 if {$err || ($opts(-nooutput) && [string length $result])} {
552 configlog "Failed: [join $cmdline]"
553 configlog $result
554 configlog "============"
555 configlog "The failed code was:"
556 configlog $lines
557 configlog "============"
558 set ok 0
559 } elseif {$::autosetup(debug)} {
560 configlog "Compiled OK: [join $cmdline]"
561 configlog "============"
562 configlog $lines
563 configlog "============"
565 file delete $src
566 file delete $tmp
568 # cache it
569 set ::cc_cache($cmdline,$lines) $ok
571 return $ok
574 # @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*?
576 # Deprecated - see 'make-config-header'
577 proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} {
578 user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead"
579 make-config-header $file -auto $autopatterns -bare $barepatterns
582 # @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ...
584 # Examines all defined variables which match the given patterns
585 # and writes an include file, '$file', which defines each of these.
586 # Variables which match '-auto' are output as follows:
587 # - defines which have the value '0' are ignored.
588 # - defines which have integer values are defined as the integer value.
589 # - any other value is defined as a string, e.g. '"value"'
590 # Variables which match '-bare' are defined as-is.
591 # Variables which match '-str' are defined as a string, e.g. '"value"'
592 # Variables which match '-none' are omitted.
594 # Note that order is important. The first pattern that matches is selected.
595 # Default behaviour is:
597 ## -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none *
599 # If the file would be unchanged, it is not written.
600 proc make-config-header {file args} {
601 set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]]
602 file mkdir [file dirname $file]
603 set lines {}
604 lappend lines "#ifndef $guard"
605 lappend lines "#define $guard"
607 # Add some defaults
608 lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_*
610 foreach n [lsort [dict keys [all-defines]]] {
611 set value [get-define $n]
612 set type [calc-define-output-type $n $args]
613 switch -exact -- $type {
614 -bare {
615 # Just output the value unchanged
617 -none {
618 continue
620 -str {
621 set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
623 -auto {
624 # Automatically determine the type
625 if {$value eq "0"} {
626 lappend lines "/* #undef $n */"
627 continue
629 if {![string is integer -strict $value]} {
630 set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
633 "" {
634 continue
636 default {
637 autosetup-error "Unknown type in make-config-header: $type"
640 lappend lines "#define $n $value"
642 lappend lines "#endif"
643 set buf [join $lines \n]
644 write-if-changed $file $buf {
645 msg-result "Created $file"
649 proc calc-define-output-type {name spec} {
650 foreach {type patterns} $spec {
651 foreach pattern $patterns {
652 if {[string match $pattern $name]} {
653 return $type
657 return ""
660 # Initialise some values from the environment or commandline or default settings
661 foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS {CFLAGS "-g -O2"}} {
662 lassign $i var default
663 define $var [get-env $var $default]
666 if {[env-is-set CC]} {
667 # Set by the user, so don't try anything else
668 set try [list [get-env CC ""]]
669 } else {
670 # Try some reasonable options
671 set try [list [get-define cross]cc [get-define cross]gcc]
673 define CC [find-an-executable {*}$try]
674 if {[get-define CC] eq ""} {
675 user-error "Could not find a C compiler. Tried: [join $try ", "]"
678 define CPP [get-env CPP "[get-define CC] -E"]
680 # XXX: Could avoid looking for a C++ compiler until requested
681 # Note that if CXX isn't found, we just set it to "false". It might not be needed.
682 if {[env-is-set CXX]} {
683 define CXX [find-an-executable -required [get-env CXX ""]]
684 } else {
685 define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++ false]
688 # CXXFLAGS default to CFLAGS if not specified
689 define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]]
691 # May need a CC_FOR_BUILD, so look for one
692 define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false]
694 if {[get-define CC] eq ""} {
695 user-error "Could not find a C compiler. Tried: [join $try ", "]"
698 define CCACHE [find-an-executable [get-env CCACHE ccache]]
700 # Initial cctest settings
701 cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0}
702 set autosetup(cc-include-deps) {}
704 msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS]"
705 if {[get-define CXX] ne "false"} {
706 msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS]"
708 msg-result "Build C compiler...[get-define CC_FOR_BUILD]"
710 # On Darwin, we prefer to use -g0 to avoid creating .dSYM directories
711 # but some compilers may not support it, so test here.
712 switch -glob -- [get-define host] {
713 *-*-darwin* {
714 if {[cctest -cflags {-g0}]} {
715 define cc-default-debug -g0
720 if {![cc-check-includes stdlib.h]} {
721 user-error "Compiler does not work. See config.log"