source: git/ppcc/autosetup/cc.tcl @ 54b24c

spielwiese
Last change on this file since 54b24c was 54b24c, checked in by Reimer Behrends <behrends@…>, 5 years ago
Finalizing thread support.
  • Property mode set to 100644
File size: 19.4 KB
Line 
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-config-header' 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# Checks for the existence of the given function by linking
34#
35proc cctest_function {function} {
36        cctest -link 1 -declare "extern void $function\(void);" -code "$function\();"
37}
38
39# Checks for the existence of the given type by compiling
40proc cctest_type {type} {
41        cctest -code "$type _x;"
42}
43
44# Checks for the existence of the given type/structure member.
45# e.g. "struct stat.st_mtime"
46proc 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);"
50}
51
52# Checks for the existence of the given define by compiling
53#
54proc cctest_define {name} {
55        cctest -code "#ifndef $name\n#error not defined\n#endif"
56}
57
58# Checks for the existence of the given name either as
59# a macro (#define) or an rvalue (such as an enum)
60#
61proc cctest_decl {name} {
62        cctest -code "#ifndef $name\n(void)$name;\n#endif"
63}
64
65# @cc-check-sizeof type ...
66#
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.
71#
72proc 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
81                        }
82                }
83                msg-result $size
84                set define [feature-define-name $type SIZEOF_]
85                define $define $size
86        }
87        # Return the last result
88        get-define $define
89}
90
91# Checks for each feature in $list by using the given script.
92#
93# When the script is evaluated, $each is set to the feature
94# being checked, and $extra is set to any additional cctest args.
95#
96# Returns 1 if all features were found, or 0 otherwise.
97proc cc-check-some-feature {list script} {
98        set ret 1
99        foreach each $list {
100                if {![check-feature $each $script]} {
101                        set ret 0
102                }
103        }
104        return $ret
105}
106
107# @cc-check-includes includes ...
108#
109# Checks that the given include files can be used.
110proc 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
119                                }
120                        }
121                }
122                if {[llength $with]} {
123                        cc-with [list -includes $with] {
124                                cctest -includes $each
125                        }
126                } else {
127                        cctest -includes $each
128                }
129        }
130}
131
132# @cc-include-needs include required ...
133#
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'.
136proc cc-include-needs {file args} {
137        foreach depfile $args {
138                dict set ::autosetup(cc-include-deps) $file $depfile 1
139        }
140}
141
142# @cc-check-types type ...
143#
144# Checks that the types exist.
145proc cc-check-types {args} {
146        cc-check-some-feature $args {
147                cctest_type $each
148        }
149}
150
151# @cc-check-defines define ...
152#
153# Checks that the given preprocessor symbols are defined.
154proc cc-check-defines {args} {
155        cc-check-some-feature $args {
156                cctest_define $each
157        }
158}
159
160# @cc-check-decls name ...
161#
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'.
165proc 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
176                }
177        }
178        return $ret
179}
180
181# @cc-check-functions function ...
182#
183# Checks that the given functions exist (can be linked).
184proc cc-check-functions {args} {
185        cc-check-some-feature $args {
186                cctest_function $each
187        }
188}
189
190# @cc-check-members type.member ...
191#
192# Checks that the given type/structure members exist.
193# A structure member is of the form 'struct stat.st_mtime'.
194proc cc-check-members {args} {
195        cc-check-some-feature $args {
196                cctest_member $each
197        }
198}
199
200# @cc-check-function-in-lib function libs ?otherlibs?
201#
202# Checks that the given function can be found in one of the libs.
203#
204# First checks for no library required, then checks each of the libraries
205# in turn.
206#
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.
210#
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'.
214#
215# Returns 1 if found or 0 if not.
216#
217proc 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
235                                        }
236                                }
237                        }
238                }
239        }
240        define-feature $function $found
241        if {!$found} {
242                msg-result "no"
243        }
244        return $found
245}
246
247# @cc-check-tools tool ...
248#
249# Checks for existence of the given compiler tools, taking
250# into account any cross compilation prefix.
251#
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.
259#
260# It is an error if the executable is not found.
261#
262proc cc-check-tools {args} {
263        foreach tool $args {
264                msg-checking "Checking for $tool..."
265                set TOOL [string toupper $tool]
266                set exe [get-env $TOOL [get-define cross]$tool]
267                if {[find-executable {*}$exe]} {
268                        define $TOOL $exe
269                        msg-result $exe
270                        continue
271                }
272                if {[find-executable {*}$tool]} {
273                        define $TOOL $tool
274                        msg-result $tool
275                        msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect"
276                        continue
277                }
278                user-error "Failed to find $exe"
279        }
280}
281
282# @cc-check-progs prog ...
283#
284# Checks for existence of the given executables on the path.
285#
286# For example, when checking for 'grep', the path is searched for
287# the executable, 'grep', and if found 'GREP' is defined as 'grep'.
288#
289# If the executable is not found, the variable is defined as 'false'.
290# Returns 1 if all programs were found, or 0 otherwise.
291#
292proc cc-check-progs {args} {
293        set failed 0
294        foreach prog $args {
295                set PROG [string toupper $prog]
296                msg-checking "Checking for $prog..."
297                if {![find-executable $prog]} {
298                        msg-result no
299                        define $PROG false
300                        incr failed
301                } else {
302                        msg-result ok
303                        define $PROG $prog
304                }
305        }
306        expr {!$failed}
307}
308
309# @cc-path-progs prog ...
310#
311# Like cc-check-progs, but sets the define to the full path rather
312# than just the program name.
313#
314proc cc-path-progs {args} {
315        set failed 0
316        foreach prog $args {
317                set PROG [string toupper $prog]
318                msg-checking "Checking for $prog..."
319                set path [find-executable-path $prog]
320                if {$path eq ""} {
321                        msg-result no
322                        define $PROG false
323                        incr failed
324                } else {
325                        msg-result $path
326                        define $PROG $path
327                }
328        }
329        expr {!$failed}
330}
331
332# Adds the given settings to $::autosetup(ccsettings) and
333# returns the old settings.
334#
335proc cc-add-settings {settings} {
336        if {[llength $settings] % 2} {
337                autosetup-error "settings list is missing a value: $settings"
338        }
339
340        set prev [cc-get-settings]
341        # workaround a bug in some versions of jimsh by forcing
342        # conversion of $prev to a list
343        llength $prev
344
345        array set new $prev
346
347        foreach {name value} $settings {
348                switch -exact -- $name {
349                        -cflags - -includes {
350                                # These are given as lists
351                                lappend new($name) {*}[list-non-empty $value]
352                        }
353                        -declare {
354                                lappend new($name) $value
355                        }
356                        -libs {
357                                # Note that new libraries are added before previous libraries
358                                set new($name) [list {*}[list-non-empty $value] {*}$new($name)]
359                        }
360                        -link - -lang - -nooutput {
361                                set new($name) $value
362                        }
363                        -source - -sourcefile - -code {
364                                # XXX: These probably are only valid directly from cctest
365                                set new($name) $value
366                        }
367                        default {
368                                autosetup-error "unknown cctest setting: $name"
369                        }
370                }
371        }
372
373        cc-store-settings [array get new]
374
375        return $prev
376}
377
378proc cc-store-settings {new} {
379        set ::autosetup(ccsettings) $new
380}
381
382proc cc-get-settings {} {
383        return $::autosetup(ccsettings)
384}
385
386# Similar to cc-add-settings, but each given setting
387# simply replaces the existing value.
388#
389# Returns the previous settings
390proc cc-update-settings {args} {
391        set prev [cc-get-settings]
392        cc-store-settings [dict merge $prev $args]
393        return $prev
394}
395
396# @cc-with settings ?{ script }?
397#
398# Sets the given 'cctest' settings and then runs the tests in '$script'.
399# Note that settings such as '-lang' replace the current setting, while
400# those such as '-includes' are appended to the existing setting.
401#
402# If no script is given, the settings become the default for the remainder
403# of the 'auto.def' file.
404#
405## cc-with {-lang c++} {
406##   # This will check with the C++ compiler
407##   cc-check-types bool
408##   cc-with {-includes signal.h} {
409##     # This will check with the C++ compiler, signal.h and any existing includes.
410##     ...
411##   }
412##   # back to just the C++ compiler
413## }
414#
415# The '-libs' setting is special in that newer values are added *before* earlier ones.
416#
417## cc-with {-libs {-lc -lm}} {
418##   cc-with {-libs -ldl} {
419##     cctest -libs -lsocket ...
420##     # libs will be in this order: -lsocket -ldl -lc -lm
421##   }
422## }
423proc cc-with {settings args} {
424        if {[llength $args] == 0} {
425                cc-add-settings $settings
426        } elseif {[llength $args] > 1} {
427                autosetup-error "usage: cc-with settings ?script?"
428        } else {
429                set save [cc-add-settings $settings]
430                set rc [catch {uplevel 1 [lindex $args 0]} result info]
431                cc-store-settings $save
432                if {$rc != 0} {
433                        return -code [dict get $info -code] $result
434                }
435                return $result
436        }
437}
438
439# @cctest ?settings?
440#
441# Low level C/C++ compiler checker. Compiles and or links a small C program
442# according to the arguments and returns 1 if OK, or 0 if not.
443#
444# Supported settings are:
445#
446## -cflags cflags      A list of flags to pass to the compiler
447## -includes list      A list of includes, e.g. {stdlib.h stdio.h}
448## -declare code       Code to declare before main()
449## -link 1             Don't just compile, link too
450## -lang c|c++         Use the C (default) or C++ compiler
451## -libs liblist       List of libraries to link, e.g. {-ldl -lm}
452## -code code          Code to compile in the body of main()
453## -source code        Compile a complete program. Ignore -includes, -declare and -code
454## -sourcefile file    Shorthand for -source [readfile [get-define srcdir]/$file]
455## -nooutput 1         Treat any compiler output (e.g. a warning) as an error
456#
457# Unless '-source' or '-sourcefile' is specified, the C program looks like:
458#
459## #include <firstinclude>   /* same for remaining includes in the list */
460##
461## declare-code              /* any code in -declare, verbatim */
462##
463## int main(void) {
464##   code                    /* any code in -code, verbatim */
465##   return 0;
466## }
467#
468# Any failures are recorded in 'config.log'
469#
470proc cctest {args} {
471        set src conftest__.c
472        set tmp conftest__
473
474        # Easiest way to merge in the settings
475        cc-with $args {
476                array set opts [cc-get-settings]
477        }
478
479        if {[info exists opts(-sourcefile)]} {
480                set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"]
481        }
482        if {[info exists opts(-source)]} {
483                set lines $opts(-source)
484        } else {
485                foreach i $opts(-includes) {
486                        if {$opts(-code) ne "" && ![feature-checked $i]} {
487                                # Compiling real code with an unchecked header file
488                                # Quickly (and silently) check for it now
489
490                                # Remove all -includes from settings before checking
491                                set saveopts [cc-update-settings -includes {}]
492                                msg-quiet cc-check-includes $i
493                                cc-store-settings $saveopts
494                        }
495                        if {$opts(-code) eq "" || [have-feature $i]} {
496                                lappend source "#include <$i>"
497                        }
498                }
499                lappend source {*}$opts(-declare)
500                lappend source "int main(void) {"
501                lappend source $opts(-code)
502                lappend source "return 0;"
503                lappend source "}"
504
505                set lines [join $source \n]
506        }
507
508        # Build the command line
509        set cmdline {}
510        switch -exact -- $opts(-lang) {
511                c++ {
512                        lappend cmdline {*}[get-define CXX] {*}[get-define CXXFLAGS]
513                }
514                c {
515                        lappend cmdline {*}[get-define CC] {*}[get-define CFLAGS]
516                }
517                default {
518                        autosetup-error "cctest called with unknown language: $opts(-lang)"
519                }
520        }
521
522        if {$opts(-link)} {
523                lappend cmdline {*}[get-define LDFLAGS]
524        } else {
525                set tmp conftest__.o
526                lappend cmdline -c
527        }
528        lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""]
529        lappend cmdline $src -o $tmp {*}$opts(-libs)
530        if {$opts(-link)} {
531                lappend cmdline {*}[get-define LIBS]
532        }
533
534        # At this point we have the complete command line and the
535        # complete source to be compiled. Get the result from cache if
536        # we can
537        if {[info exists ::cc_cache($cmdline,$lines)]} {
538                msg-checking "(cached) "
539                set ok $::cc_cache($cmdline,$lines)
540                if {$::autosetup(debug)} {
541                        configlog "From cache (ok=$ok): [join $cmdline]"
542                        configlog "============"
543                        configlog $lines
544                        configlog "============"
545                }
546                return $ok
547        }
548
549        writefile $src $lines\n
550
551        set ok 1
552        set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
553        if {$err || ($opts(-nooutput) && [string length $result])} {
554                configlog "Failed: [join $cmdline]"
555                configlog $result
556                configlog "============"
557                configlog "The failed code was:"
558                configlog $lines
559                configlog "============"
560                set ok 0
561        } elseif {$::autosetup(debug)} {
562                configlog "Compiled OK: [join $cmdline]"
563                configlog "============"
564                configlog $lines
565                configlog "============"
566        }
567        file delete $src
568        file delete $tmp
569
570        # cache it
571        set ::cc_cache($cmdline,$lines) $ok
572
573        return $ok
574}
575
576# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*?
577#
578# Deprecated - see 'make-config-header'
579proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} {
580        user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead"
581        make-config-header $file -auto $autopatterns -bare $barepatterns
582}
583
584# @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ...
585#
586# Examines all defined variables which match the given patterns
587# and writes an include file, '$file', which defines each of these.
588# Variables which match '-auto' are output as follows:
589# - defines which have the value '0' are ignored.
590# - defines which have integer values are defined as the integer value.
591# - any other value is defined as a string, e.g. '"value"'
592# Variables which match '-bare' are defined as-is.
593# Variables which match '-str' are defined as a string, e.g. '"value"'
594# Variables which match '-none' are omitted.
595#
596# Note that order is important. The first pattern that matches is selected.
597# Default behaviour is:
598#
599##  -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none *
600#
601# If the file would be unchanged, it is not written.
602proc make-config-header {file args} {
603        set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]]
604        file mkdir [file dirname $file]
605        set lines {}
606        lappend lines "#ifndef $guard"
607        lappend lines "#define $guard"
608
609        # Add some defaults
610        lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_*
611
612        foreach n [lsort [dict keys [all-defines]]] {
613                set value [get-define $n]
614                set type [calc-define-output-type $n $args]
615                switch -exact -- $type {
616                        -bare {
617                                # Just output the value unchanged
618                        }
619                        -none {
620                                continue
621                        }
622                        -str {
623                                set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
624                        }
625                        -auto {
626                                # Automatically determine the type
627                                if {$value eq "0"} {
628                                        lappend lines "/* #undef $n */"
629                                        continue
630                                }
631                                if {![string is integer -strict $value]} {
632                                        set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
633                                }
634                        }
635                        "" {
636                                continue
637                        }
638                        default {
639                                autosetup-error "Unknown type in make-config-header: $type"
640                        }
641                }
642                lappend lines "#define $n $value"
643        }
644        lappend lines "#endif"
645        set buf [join $lines \n]
646        write-if-changed $file $buf {
647                msg-result "Created $file"
648        }
649}
650
651proc calc-define-output-type {name spec} {
652        foreach {type patterns} $spec {
653                foreach pattern $patterns {
654                        if {[string match $pattern $name]} {
655                                return $type
656                        }
657                }
658        }
659        return ""
660}
661
662# Initialise some values from the environment or commandline or default settings
663foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS CFLAGS} {
664        lassign $i var default
665        define $var [get-env $var $default]
666}
667
668if {[env-is-set CC]} {
669        # Set by the user, so don't try anything else
670        set try [list [get-env CC ""]]
671} else {
672        # Try some reasonable options
673        set try [list [get-define cross]gcc [get-define cross]clang [get-define cross]cc]
674}
675define CC [find-an-executable {*}$try]
676if {[get-define CC] eq ""} {
677        user-error "Could not find a C compiler. Tried: [join $try ", "]"
678}
679
680define CPP [get-env CPP "[get-define CC] -E"]
681
682# XXX: Could avoid looking for a C++ compiler until requested
683# Note that if CXX isn't found, we just set it to "false". It might not be needed.
684if {[env-is-set CXX]} {
685        define CXX [find-an-executable -required [get-env CXX ""]]
686} else {
687        define CXX [find-an-executable [get-define cross]g++ [get-define cross]clang++ [get-define cross]c++ false]
688}
689
690# CXXFLAGS default to CFLAGS if not specified
691define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]]
692
693# May need a CC_FOR_BUILD, so look for one
694define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] gcc clang cc false]
695
696if {[get-define CC] eq ""} {
697        user-error "Could not find a C compiler. Tried: [join $try ", "]"
698}
699
700define CCACHE [find-an-executable [get-env CCACHE ccache]]
701
702# Initial cctest settings
703cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0}
704set autosetup(cc-include-deps) {}
705
706msg-result "C compiler...[get-define CC] [get-define CFLAGS]"
707if {[get-define CXX] ne "false"} {
708        msg-result "C++ compiler...[get-define CXX] [get-define CXXFLAGS]"
709}
710msg-result "Build C compiler...[get-define CC_FOR_BUILD]"
711
712# On Darwin, we prefer to use -g0 to avoid creating .dSYM directories
713# but some compilers may not support it, so test here.
714switch -glob -- [get-define host] {
715        *-*-darwin* {
716                if {[cctest -cflags {-g0}]} {
717                        define cc-default-debug -g0
718                }
719        }
720}
721
722if {![cc-check-includes stdlib.h]} {
723        user-error "Compiler does not work. See config.log"
724}
Note: See TracBrowser for help on using the repository browser.