source: git/ppcc/autosetup/cc.tcl

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